From bd67006f6358c7883f1b6fd3caa11f8536623a5e Mon Sep 17 00:00:00 2001 From: Aditya Bist Date: Fri, 23 Mar 2018 16:38:48 -0700 Subject: [PATCH] Feature/agent1 adbist (#973) * WIP * wip * SQL Agent wip * wip * Initial control host (wip) * Initial hookup of SQL Agent service to job component * Update agent package.json * Hook up getJobs call * A couple job view updates * Add some more agent views * added back button, run actions and overview accordion * refactoring * overview table complete * fixed the dropdown arrow for the overview section * added table for prev job list * fixed agent job result type * Rename some 'agent' classes to 'jobManagement' * code cleaning and code review comments * fixed yarn.lock conflicts * added function for job history * changed vscode-languageclient version * changed yarn lock file * fixed yarn lock file * fixed yarn file * fixed css paths * added images to packaging step * fix resource path for packaging * job history page (#852) * added back button, run actions and overview accordion * refactoring * overview table complete * fixed the dropdown arrow for the overview section * added table for prev job list * fixed agent job result type * code cleaning and code review comments * fixed yarn.lock conflicts * added function for job history * changed vscode-languageclient version * changed yarn lock file * fixed yarn lock file * fixed yarn file * fixed css paths * added images to packaging step * fix resource path for packaging * added steps lists * fixed style and dimensions * fixed conflicts * Switch back getJobs return type * Make enum const * Remove sqlops const * WIP * WIP * implemented job list * added the Date and Status columns * update yarn files * merged feature/agent1 * added theme styling for light theme * changed yarn lock files * Feature/agent1 adbist (#899) * added back button, run actions and overview accordion * refactoring * overview table complete * fixed the dropdown arrow for the overview section * added table for prev job list * fixed agent job result type * code cleaning and code review comments * fixed yarn.lock conflicts * added function for job history * changed vscode-languageclient version * changed yarn lock file * fixed yarn lock file * fixed yarn file * fixed css paths * added images to packaging step * fix resource path for packaging * added steps lists * fixed style and dimensions * fixed conflicts * implemented job list * added the Date and Status columns * update yarn files * merged feature/agent1 * added theme styling for light theme * changed yarn lock files * made job history page css more specific * Add visiblity check to job view * added method signatures for job history with DMP * Clean up jobs styling and call getJobHistory * Add more Job Table styling * Enable detail view in job table * Use updated slickgrid repo * vbumped slickgrid * added methods for job running * added job actions to sqlops * Convert rowdetail slickgrid plug to TypeScript * Feature/agent1 adbist (#945) * added back button, run actions and overview accordion * refactoring * overview table complete * fixed the dropdown arrow for the overview section * added table for prev job list * fixed agent job result type * code cleaning and code review comments * fixed yarn.lock conflicts * added function for job history * changed vscode-languageclient version * changed yarn lock file * fixed yarn lock file * fixed yarn file * fixed css paths * added images to packaging step * fix resource path for packaging * added steps lists * fixed style and dimensions * fixed conflicts * implemented job list * added the Date and Status columns * update yarn files * merged feature/agent1 * added theme styling for light theme * changed yarn lock files * added method signatures for job history with DMP * added methods for job running * added job actions to sqlops * Refer to dataprotocol from feature/agentDmp1 branch * Update SQL Tools version to 1.4.0-alpha.13 * Change Feb to March in release note prompt * SQL Agent extension metadata * add feature explicitly in client creation * Update Agent job registration * navigation works but is really slow to load data * Update package.json * fixed conflicts * Feature/agent1 adbist (#955) * added back button, run actions and overview accordion * refactoring * overview table complete * fixed the dropdown arrow for the overview section * added table for prev job list * fixed agent job result type * code cleaning and code review comments * fixed yarn.lock conflicts * added function for job history * changed vscode-languageclient version * changed yarn lock file * fixed yarn lock file * fixed yarn file * fixed css paths * added images to packaging step * fix resource path for packaging * added steps lists * fixed style and dimensions * fixed conflicts * implemented job list * added the Date and Status columns * update yarn files * merged feature/agent1 * added theme styling for light theme * changed yarn lock files * added method signatures for job history with DMP * added methods for job running * added job actions to sqlops * navigation works but is really slow to load data * Add jobs view icon * fixed bug where not all steps were being shown * added more to history page * added loadHistories and code review comments * made the params standard * fixed json local paths --- .../views/jobHistory.component.html | 44 ++++++------ .../views/jobHistory.component.ts | 70 +++++++++++++------ .../parts/jobManagement/views/jobHistory.css | 2 +- .../jobManagement/views/jobHistoryTree.ts | 36 ++++------ .../jobManagement/views/jobsView.component.ts | 65 ++++------------- src/sql/sqlops.d.ts | 10 +-- 6 files changed, 103 insertions(+), 124 deletions(-) diff --git a/src/sql/parts/jobManagement/views/jobHistory.component.html b/src/sql/parts/jobManagement/views/jobHistory.component.html index 642f84f64d..43da359029 100644 --- a/src/sql/parts/jobManagement/views/jobHistory.component.html +++ b/src/sql/parts/jobManagement/views/jobHistory.component.html @@ -15,10 +15,10 @@ @@ -34,10 +34,10 @@
- User: + Category: - + {{agentJobInfo.category}} Enabled: @@ -48,30 +48,30 @@
- Alert: + Has Alert: - + {{agentJobInfo.hasTarget}} - Notification: + Has Schedule: - + {{agentJobInfo.hasSchedule}}
- Schedule: + Last Run: - + {{agentJobInfo.lastRun}} - Target Server: + Next Run: - + {{agentJobInfo.nextRun}}
@@ -98,31 +98,31 @@

- {{agentJobInfo.lastRun}} + {{agentJobHistoryInfo?.runDate}}

- - - @@ -137,7 +137,7 @@ - @@ -145,8 +145,8 @@ -

Status:

+

- Error Message: + Message: - + + {{agentJobHistoryInfo?.message}}
Duration: - + + {{agentJobHistoryInfo?.runDuration}}
SQL message ID: +
Retries Attempted: - + + {{agentJobHistoryInfo?.retriesAttempted}}
diff --git a/src/sql/parts/jobManagement/views/jobHistory.component.ts b/src/sql/parts/jobManagement/views/jobHistory.component.ts index e36d39973a..ebc5775a04 100644 --- a/src/sql/parts/jobManagement/views/jobHistory.component.ts +++ b/src/sql/parts/jobManagement/views/jobHistory.component.ts @@ -5,25 +5,20 @@ import 'vs/css!./jobHistory'; -import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, Input } from '@angular/core'; -import { ICancelableEvent } from 'vs/base/parts/tree/browser/treeDefaults'; +import { OnInit, Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, OnDestroy, ViewChild, Input, Injectable } from '@angular/core'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { attachListStyler } from 'vs/platform/theme/common/styler'; -import { getContentHeight } from 'vs/base/browser/dom'; import { Tree } from 'vs/base/parts/tree/browser/treeImpl'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; import { PanelComponent } from 'sql/base/browser/ui/panel/panel.component'; import { IBootstrapService, BOOTSTRAP_SERVICE_ID } from 'sql/services/bootstrap/bootstrapService'; import { IJobManagementService } from '../common/interfaces'; -import { ExplorerDataSource } from 'sql/parts/dashboard/widgets/explorer/explorerTree'; -import { TreeCreationUtils } from 'sql/parts/registeredServer/viewlet/treeCreationUtils'; import { DashboardServiceInterface } from 'sql/parts/dashboard/services/dashboardServiceInterface.service'; import { AgentViewComponent } from 'sql/parts/jobManagement/agent/agentView.component'; import { JobHistoryController, JobHistoryDataSource, JobHistoryRenderer, JobHistoryFilter, JobHistoryModel, JobHistoryRow } from 'sql/parts/jobManagement/views/jobHistoryTree'; import { AgentJobHistoryInfo, AgentJobInfo } from 'sqlops'; -import { toDisposableSubscription } from '../../common/rxjsUtils'; export const DASHBOARD_SELECTOR: string = 'jobhistory-component'; @@ -46,9 +41,8 @@ export class JobHistoryComponent extends Disposable implements OnInit, OnDestroy @Input() public agentJobInfo: AgentJobInfo = undefined; @Input() public jobId: string = undefined; @Input() public agentJobHistoryInfo: AgentJobHistoryInfo = undefined; - private prevJobId: string = undefined; - private jobName: string = undefined; + private prevJobId: string = undefined; private isVisible: boolean = false; @@ -65,8 +59,32 @@ export class JobHistoryComponent extends Disposable implements OnInit, OnDestroy ngOnInit() { let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; - this.loadHistory(); - this._treeDataSource.data = []; + const self = this; + this._treeController.onClick = (tree, element, event, origin = 'mouse') => { + const payload = { origin: origin }; + const isDoubleClick = (origin === 'mouse' && event.detail === 2); + // Cancel Event + const isMouseDown = event && event.browserEvent && event.browserEvent.type === 'mousedown'; + + if (!isMouseDown) { + event.preventDefault(); // we cannot preventDefault onMouseDown because this would break DND otherwise + } + + event.stopPropagation(); + + tree.setFocus(element, payload); + + if (element && isDoubleClick) { + event.preventDefault(); // focus moves to editor, we need to prevent default + } else { + tree.setFocus(element, payload); + tree.setSelection([element], payload); + self.agentJobHistoryInfo = self._treeController.jobHistories.filter(history => history.instanceId === element.instanceID)[0]; + self.agentJobHistoryInfo.runDate = self.formatTime(self.agentJobHistoryInfo.runDate); + self._cd.detectChanges(); + } + return true; + }; this._tree = new Tree(this._tableContainer.nativeElement, { controller: this._treeController, dataSource: this._treeDataSource, @@ -75,7 +93,6 @@ export class JobHistoryComponent extends Disposable implements OnInit, OnDestroy }); this._register(attachListStyler(this._tree, this.bootstrapService.themeService)); this._tree.layout(1024); - //this._tree.setInput(new JobHistoryModel()); } ngOnDestroy() { @@ -83,7 +100,7 @@ export class JobHistoryComponent extends Disposable implements OnInit, OnDestroy ngAfterContentChecked() { if (this.isVisible === false && this._tableContainer.nativeElement.offsetParent !== null) { - if (this.prevJobId !== undefined && this.prevJobId !== this.jobId) { + if (this.prevJobId !== this.jobId) { this.loadHistory(); this.prevJobId = this.jobId; } @@ -91,12 +108,17 @@ export class JobHistoryComponent extends Disposable implements OnInit, OnDestroy } loadHistory() { + const self = this; let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; this._jobManagementService.getJobHistory(ownerUri, this.jobId).then((result) => { if (result.jobs) { - let jobHistory = result.jobs; - this._treeDataSource.data = jobHistory.map(job => this.convertToJobHistoryRow(job)); - this._tree.setInput(new JobHistoryModel()); + self._treeController.jobHistories = result.jobs; + let jobHistoryRows = self._treeController.jobHistories.map(job => self.convertToJobHistoryRow(job)); + self._treeDataSource.data = jobHistoryRows; + self._tree.setInput(new JobHistoryModel()); + self.agentJobHistoryInfo = self._treeController.jobHistories[0]; + self.agentJobHistoryInfo.runDate = self.formatTime(self.agentJobHistoryInfo.runDate); + self._cd.detectChanges(); } }); } @@ -111,22 +133,26 @@ export class JobHistoryComponent extends Disposable implements OnInit, OnDestroy } } - private jobAction(action: string): void { + private jobAction(action: string, jobName: string): void { let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; - this._jobManagementService.jobAction(ownerUri, 'jobName', action); + this._jobManagementService.jobAction(ownerUri, jobName, action); } private goToJobs(): void { + this.isVisible = false; this._agentViewComponent.showHistory = false; } private convertToJobHistoryRow(historyInfo: AgentJobHistoryInfo): JobHistoryRow { - let jobHistoryRow = { - runDate: historyInfo.runDate, - runStatus: JobHistoryRow.convertToStatusString(historyInfo.runStatus), - jobID: historyInfo.jobID - }; + let jobHistoryRow = new JobHistoryRow(); + jobHistoryRow.runDate = historyInfo.runDate; + jobHistoryRow.runStatus = JobHistoryRow.convertToStatusString(historyInfo.runStatus); + jobHistoryRow.instanceID = historyInfo.instanceId; return jobHistoryRow; } + + private formatTime(time: string): string { + return time.replace('T', ' '); + } } diff --git a/src/sql/parts/jobManagement/views/jobHistory.css b/src/sql/parts/jobManagement/views/jobHistory.css index 0fdc781484..5ff2b29c6e 100644 --- a/src/sql/parts/jobManagement/views/jobHistory.css +++ b/src/sql/parts/jobManagement/views/jobHistory.css @@ -158,7 +158,7 @@ input#accordion:checked ~ .accordion-content { } table.step-list tr.step-row td { - padding-right: 100px; + padding-right: 10px; } .history-details { diff --git a/src/sql/parts/jobManagement/views/jobHistoryTree.ts b/src/sql/parts/jobManagement/views/jobHistoryTree.ts index 75b8eba4be..de2f3e484f 100644 --- a/src/sql/parts/jobManagement/views/jobHistoryTree.ts +++ b/src/sql/parts/jobManagement/views/jobHistoryTree.ts @@ -26,11 +26,14 @@ import { generateUuid } from 'vs/base/common/uuid'; import * as DOM from 'vs/base/browser/dom'; import { OEAction } from 'sql/parts/registeredServer/viewlet/objectExplorerActions'; import { Builder, $, withElementById } from 'vs/base/browser/builder'; +import { AgentJobHistoryInfo } from 'sqlops'; +import { Agent } from 'vs/base/node/request'; export class JobHistoryRow { runDate: string; runStatus: string; - jobID: string; + instanceID: number; + rowID: string = generateUuid(); public static convertToStatusString(status: number): string { switch(status) { @@ -47,28 +50,9 @@ export class JobHistoryModel { } export class JobHistoryController extends TreeDefaults.DefaultController { + private _jobHistories: AgentJobHistoryInfo[]; protected onLeftClick(tree: tree.ITree, element: JobHistoryRow, event: IMouseEvent, origin: string = 'mouse'): boolean { - const payload = { origin: origin }; - const isDoubleClick = (origin === 'mouse' && event.detail === 2); - // Cancel Event - const isMouseDown = event && event.browserEvent && event.browserEvent.type === 'mousedown'; - - if (!isMouseDown) { - event.preventDefault(); // we cannot preventDefault onMouseDown because this would break DND otherwise - } - - event.stopPropagation(); - - tree.setFocus(element, payload); - - if (element && isDoubleClick) { - event.preventDefault(); // focus moves to editor, we need to prevent default - } else { - tree.setFocus(element, payload); - tree.setSelection([element], payload); - } - return true; } @@ -76,6 +60,14 @@ export class JobHistoryController extends TreeDefaults.DefaultController { return true; } + public set jobHistories(value: AgentJobHistoryInfo[]) { + this._jobHistories = value; + } + + public get jobHistories(): AgentJobHistoryInfo[] { + return this._jobHistories; + } + } export class JobHistoryDataSource implements tree.IDataSource { @@ -85,7 +77,7 @@ export class JobHistoryDataSource implements tree.IDataSource { if (element instanceof JobHistoryModel) { return JobHistoryModel.id; } else { - return (element as JobHistoryRow).jobID; + return (element as JobHistoryRow).rowID; } } diff --git a/src/sql/parts/jobManagement/views/jobsView.component.ts b/src/sql/parts/jobManagement/views/jobsView.component.ts index 9113c0264a..ac80d9bca5 100644 --- a/src/sql/parts/jobManagement/views/jobsView.component.ts +++ b/src/sql/parts/jobManagement/views/jobsView.component.ts @@ -93,36 +93,6 @@ export class JobsViewComponent implements OnInit, OnDestroy { } } - loadJobHistories() { - if (this.jobs) { - this.jobs.forEach((job) => { - let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; - this._jobManagementService.getJobHistory(ownerUri, job.jobId).then((result) => { - if (result.jobs) { - this.jobHistories[job.jobId] = result.jobs; - this.expandJobsWithFailures(); - } - }); - }); - } - } - - private expandJobsWithFailures(): void { - for (let i: number = 0; i < this.jobs.length; ++i) { - let job = this.jobs[i]; - let jobHistory = this.jobHistories[job.jobId]; - if (jobHistory && jobHistory.length > 0) { - let latestExecution = jobHistory[jobHistory.length - 1]; - if (latestExecution.runStatus !== 0) { - this.expandJobRowDetails(i); - } - } - } - } - - private expandJobRowDetails(rowIdx: number): void { - } - onFirstVisible() { let self = this; let columns = this.columns.map((column) => { @@ -157,11 +127,6 @@ export class JobsViewComponent implements OnInit, OnDestroy { let job = self.getJob(args); self._agentViewComponent.jobId = job.jobId; self._agentViewComponent.agentJobInfo = job; - self.getJobHistoryInfo(ownerUri, job).then(result => { - if (result) { - this._agentViewComponent.agentJobHistoryInfo = result; - } - }); self.isVisible = false; self._agentViewComponent.showHistory = true; }); @@ -176,22 +141,6 @@ export class JobsViewComponent implements OnInit, OnDestroy { }); } - getJobHistoryInfo(ownerUri: string, job: any): Thenable { - return new Promise((resolve, reject) => { - if (this.jobHistories[job.jobId]){ - Promise.resolve(this.jobHistories[job.jobId]); - } else { - this._jobManagementService.getJobHistory(ownerUri, job.jobId).then(result => { - if (result && result.jobs) { - Promise.resolve(result.jobs); - } else { - Promise.reject(undefined); - } - }); - } - }); - } - onJobsAvailable(jobs: sqlops.AgentJobInfo[]) { let jobViews = jobs.map((job) => { return { @@ -225,7 +174,6 @@ export class JobsViewComponent implements OnInit, OnDestroy { this._table.resizeCanvas(); this._table.autosizeColumns(); - this.loadJobHistories(); } @@ -246,6 +194,19 @@ export class JobsViewComponent implements OnInit, OnDestroy { ''; } + loadJobHistories() { + if (this.jobs) { + this.jobs.forEach((job) => { + let ownerUri: string = this._dashboardService.connectionManagementService.connectionInfo.ownerUri; + this._jobManagementService.getJobHistory(ownerUri, job.jobId).then((result) => { + if (result.jobs) { + this.jobHistories[job.jobId] = result.jobs; + } + }); + }); + } + } + private getJob(args: Slick.OnClickEventArgs): sqlops.AgentJobInfo { let cell = args.cell; let jobName = args.grid.getCellNode(1, cell).innerText.trim(); diff --git a/src/sql/sqlops.d.ts b/src/sql/sqlops.d.ts index ca9a851042..a3726d68a5 100644 --- a/src/sql/sqlops.d.ts +++ b/src/sql/sqlops.d.ts @@ -1061,13 +1061,13 @@ declare module 'sqlops' { } export interface AgentJobHistoryInfo { - instanceID: number; - sqlMessageID: number; + instanceId: number; + sqlMessageId: number; message: string; - stepID: number; + stepId: number; stepName: string; sqlSeverity: number; - jobID: string; + jobId: string; jobName: string; runStatus: number; runDate: string; @@ -1081,7 +1081,7 @@ declare module 'sqlops' { export interface AgentServicesProvider extends DataProvider { getJobs(connectionUri: string): Thenable; - getJobHistory(connectionUri: string, jobID: string): Thenable; + getJobHistory(connectionUri: string, jobId: string): Thenable; jobAction(connectionUri: string, jobName: string, action: string): Thenable; }