Agent Notebooks Scheduler (#6786)
* added agent notebooks, notebook history view and view materialized notebook button * Got a basic UI running for viewing notebook history * made some changes to make UI look good * Added new notebook dialog * Added new notebook Dialog * Added create notebook dialog * Added edit and delete notebook job * Added some notebook history features * Added new notebook job icons, fixed a minor bug in openmaterializednotebookAPI and added fixed the schedule Picker API. * Fixed Bugs in Notebook Grid expansion * Fixed Notebook table highlighting and grid generation is done using code. * fixed some UI bugs * Added changes to reflect sqltoolservice api * Fixed some localize keys * Made changes in the PR and added ability to open Template Notebooks from notebook history view. * Added pin and renaming to notebook history * made some library calls async * fixed an import bug caused by merging from master * Validation in NotebookJobDialog * Added entry points for scheduling notebooks on file explorer and notebook editor * Handled no active connections and a small bug in collapsing grid * fix a bug in scheduling notebook from explorer and toolbar * setting up agent providers from connection now * changed modals * Reupload edited template * Add dialog info, solved an edit bug and localized UI strings. * Bug fixes in UI, notebook renaming and editing template on fly. * fixed a bug that failed editing notebook jobs from notebook jobs table * Fixed a cyclic dependency, made strings const and some other changes in the PR * Made some cyclic dependency and some fixes from PR * made some changes mentioned in the PR * Changed storage database health text * Changed the sqltoolservice version to the point to the latest build.
@@ -395,6 +395,30 @@ export class MainThreadDataProtocol extends Disposable implements MainThreadData
|
||||
deleteJobStep(connectionUri: string, stepInfo: azdata.AgentJobStepInfo): Thenable<azdata.ResultStatus> {
|
||||
return self._proxy.$deleteJobStep(handle, connectionUri, stepInfo);
|
||||
},
|
||||
getNotebooks(connectionUri: string): Thenable<azdata.AgentNotebooksResult> {
|
||||
return self._proxy.$getNotebooks(handle, connectionUri);
|
||||
},
|
||||
getNotebookHistory(connectionUri: string, jobID: string, jobName: string, targetDatabase: string): Thenable<azdata.AgentNotebookHistoryResult> {
|
||||
return self._proxy.$getNotebookHistory(handle, connectionUri, jobID, jobName, targetDatabase);
|
||||
},
|
||||
getMaterializedNotebook(connectionUri: string, targetDatabase: string, notebookMaterializedId: number): Thenable<azdata.AgentNotebookMaterializedResult> {
|
||||
return self._proxy.$getMaterializedNotebook(handle, connectionUri, targetDatabase, notebookMaterializedId);
|
||||
},
|
||||
updateNotebookMaterializedName(connectionUri: string, agentNotebookHistory: azdata.AgentNotebookHistoryInfo, targetDatabase: string, name: string): Thenable<azdata.ResultStatus> {
|
||||
return self._proxy.$updateNotebookMaterializedName(handle, connectionUri, agentNotebookHistory, targetDatabase, name);
|
||||
},
|
||||
deleteMaterializedNotebook(connectionUri: string, agentNotebookHistory: azdata.AgentNotebookHistoryInfo, targetDatabase: string): Thenable<azdata.ResultStatus> {
|
||||
return self._proxy.$deleteMaterializedNotebook(handle, connectionUri, agentNotebookHistory, targetDatabase);
|
||||
},
|
||||
updateNotebookMaterializedPin(connectionUri: string, agentNotebookHistory: azdata.AgentNotebookHistoryInfo, targetDatabase: string, pin: boolean): Thenable<azdata.ResultStatus> {
|
||||
return self._proxy.$updateNotebookMaterializedPin(handle, connectionUri, agentNotebookHistory, targetDatabase, pin);
|
||||
},
|
||||
getTemplateNotebook(connectionUri: string, targetDatabase: string, jobId: string): Thenable<azdata.AgentNotebookTemplateResult> {
|
||||
return self._proxy.$getTemplateNotebook(handle, connectionUri, targetDatabase, jobId);
|
||||
},
|
||||
deleteNotebook(connectionUri: string, notebook: azdata.AgentNotebookInfo): Thenable<azdata.ResultStatus> {
|
||||
return self._proxy.$deleteNotebook(handle, connectionUri, notebook);
|
||||
},
|
||||
getAlerts(connectionUri: string): Thenable<azdata.AgentAlertsResult> {
|
||||
return self._proxy.$getAlerts(handle, connectionUri);
|
||||
},
|
||||
|
||||
@@ -98,7 +98,7 @@ class MainThreadNotebookEditor extends Disposable {
|
||||
if (!input) {
|
||||
return false;
|
||||
}
|
||||
return input === this.editor.notebookParams.input;
|
||||
return input.notebookUri.toString() === this.editor.notebookParams.input.notebookUri.toString();
|
||||
}
|
||||
|
||||
public applyEdits(versionIdCheck: number, edits: ISingleNotebookEditOperation[], opts: IUndoStopOptions): boolean {
|
||||
|
||||
@@ -679,14 +679,14 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
|
||||
* Deletes a job
|
||||
*/
|
||||
$deleteJob(handle: number, ownerUri: string, job: azdata.AgentJobInfo): Thenable<azdata.ResultStatus> {
|
||||
throw this._resolveProvider<azdata.AgentServicesProvider>(handle).deleteJob(ownerUri, job);
|
||||
return this._resolveProvider<azdata.AgentServicesProvider>(handle).deleteJob(ownerUri, job);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a job step
|
||||
*/
|
||||
$deleteJobStep(handle: number, ownerUri: string, step: azdata.AgentJobStepInfo): Thenable<azdata.ResultStatus> {
|
||||
throw this._resolveProvider<azdata.AgentServicesProvider>(handle).deleteJobStep(ownerUri, step);
|
||||
return this._resolveProvider<azdata.AgentServicesProvider>(handle).deleteJobStep(ownerUri, step);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -703,6 +703,62 @@ export class ExtHostDataProtocol extends ExtHostDataProtocolShape {
|
||||
return this._resolveProvider<azdata.AgentServicesProvider>(handle).deleteAlert(ownerUri, alert);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Agent Notebook list
|
||||
*/
|
||||
public $getNotebooks(handle: number, ownerUri: string): Thenable<azdata.AgentNotebooksResult> {
|
||||
return this._resolveProvider<azdata.AgentServicesProvider>(handle).getNotebooks(ownerUri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Agent Notebook's history
|
||||
*/
|
||||
public $getNotebookHistory(handle: number, ownerUri: string, jobID: string, jobName: string, targetDatabase: string): Thenable<azdata.AgentNotebookHistoryResult> {
|
||||
return this._resolveProvider<azdata.AgentServicesProvider>(handle).getNotebookHistory(ownerUri, jobID, jobName, targetDatabase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Agent Materialized Notebook
|
||||
*/
|
||||
public $getMaterializedNotebook(handle: number, ownerUri: string, targetDatabase: string, notebookMaterializedId: number): Thenable<azdata.AgentNotebookMaterializedResult> {
|
||||
return this._resolveProvider<azdata.AgentServicesProvider>(handle).getMaterializedNotebook(ownerUri, targetDatabase, notebookMaterializedId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Agent Template Notebook
|
||||
*/
|
||||
public $getTemplateNotebook(handle: number, ownerUri: string, targetDatabase: string, jobId: string): Thenable<azdata.AgentNotebookTemplateResult> {
|
||||
return this._resolveProvider<azdata.AgentServicesProvider>(handle).getTemplateNotebook(ownerUri, targetDatabase, jobId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete a Agent Notebook
|
||||
*/
|
||||
public $deleteNotebook(handle: number, ownerUri: string, notebook: azdata.AgentNotebookInfo): Thenable<azdata.ResultStatus> {
|
||||
return this._resolveProvider<azdata.AgentServicesProvider>(handle).deleteNotebook(ownerUri, notebook);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a Agent Materialized Notebook Name
|
||||
*/
|
||||
public $updateNotebookMaterializedName(handle: number, ownerUri: string, agentNotebookHistory: azdata.AgentNotebookHistoryInfo, targetDatabase: string, name: string): Thenable<azdata.ResultStatus> {
|
||||
return this._resolveProvider<azdata.AgentServicesProvider>(handle).updateNotebookMaterializedName(ownerUri, agentNotebookHistory, targetDatabase, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a Agent Materialized Notebook
|
||||
*/
|
||||
public $deleteMaterializedNotebook(handle: number, ownerUri: string, agentNotebookHistory: azdata.AgentNotebookHistoryInfo, targetDatabase: string): Thenable<azdata.ResultStatus> {
|
||||
return this._resolveProvider<azdata.AgentServicesProvider>(handle).deleteMaterializedNotebook(ownerUri, agentNotebookHistory, targetDatabase);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a Agent Materialized Notebook Pin
|
||||
*/
|
||||
public $updateNotebookMaterializedPin(handle: number, ownerUri: string, agentNotebookHistory: azdata.AgentNotebookHistoryInfo, targetDatabase: string, pin: boolean): Thenable<azdata.ResultStatus> {
|
||||
return this._resolveProvider<azdata.AgentServicesProvider>(handle).updateNotebookMaterializedPin(ownerUri, agentNotebookHistory, targetDatabase, pin);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get Agent Oeprators list
|
||||
*/
|
||||
|
||||
@@ -397,6 +397,47 @@ export abstract class ExtHostDataProtocolShape {
|
||||
*/
|
||||
$deleteJobStep(handle: number, ownerUri: string, step: azdata.AgentJobStepInfo): Thenable<azdata.ResultStatus> { throw ni(); }
|
||||
|
||||
/**
|
||||
* Get Agent Notebook list
|
||||
*/
|
||||
$getNotebooks(handle: number, ownerUri: string): Thenable<azdata.AgentNotebooksResult> { throw ni(); }
|
||||
|
||||
/**
|
||||
* Get a Agent Notebook's history
|
||||
*/
|
||||
$getNotebookHistory(handle: number, ownerUri: string, jobID: string, jobName: string, targetDatabase: string): Thenable<azdata.AgentNotebookHistoryResult> { throw ni(); }
|
||||
|
||||
/**
|
||||
* Get a Agent materialized notebook
|
||||
*/
|
||||
$getMaterializedNotebook(handle: number, ownerUri: string, targetDatabase: string, notebookMaterializedId: number): Thenable<azdata.AgentNotebookMaterializedResult> { throw ni(); }
|
||||
|
||||
/**
|
||||
* Get a Agent Template notebook
|
||||
*/
|
||||
$getTemplateNotebook(handle: number, ownerUri: string, targetDatabase: string, jobId: string): Thenable<azdata.AgentNotebookTemplateResult> { throw ni(); }
|
||||
|
||||
/**
|
||||
* Deletes a notebook
|
||||
*/
|
||||
$deleteNotebook(handle: number, ownerUri: string, notebook: azdata.AgentNotebookInfo): Thenable<azdata.ResultStatus> { throw ni(); }
|
||||
|
||||
/**
|
||||
* Update materialzied Notebook Name
|
||||
*/
|
||||
$updateNotebookMaterializedName(handle: number, ownerUri: string, agentNotebookHistory: azdata.AgentNotebookHistoryInfo, targetDatabase: string, name: string): Thenable<azdata.ResultStatus> { throw ni(); }
|
||||
|
||||
/**
|
||||
* Update materialzied Notebook Name
|
||||
*/
|
||||
$deleteMaterializedNotebook(handle: number, ownerUri: string, agentNotebookHistory: azdata.AgentNotebookHistoryInfo, targetDatabase: string): Thenable<azdata.ResultStatus> { throw ni(); }
|
||||
|
||||
/**
|
||||
* Update materialzied Notebook Pin
|
||||
*/
|
||||
$updateNotebookMaterializedPin(handle: number, ownerUri: string, agentNotebookHistory: azdata.AgentNotebookHistoryInfo, targetDatabase: string, pin: boolean): Thenable<azdata.ResultStatus> { throw ni(); }
|
||||
|
||||
|
||||
/**
|
||||
* Get Agent Alerts list
|
||||
*/
|
||||
|
||||
@@ -20,14 +20,13 @@ import { focusBorder, foreground } from 'vs/platform/theme/common/colorRegistry'
|
||||
import { Button } from 'sql/base/browser/ui/button/button';
|
||||
import { Color } from 'vs/base/common/color';
|
||||
|
||||
|
||||
@Component({
|
||||
selector: 'modelview-button',
|
||||
template: `
|
||||
<div>
|
||||
<label for={{this.label}}>
|
||||
<div #input style="width: 100%">
|
||||
<input #fileInput *ngIf="this.isFile === true" id={{this.label}} type="file" accept=".sql" style="display: none">
|
||||
<input #fileInput *ngIf="this.isFile === true" id={{this.label}} type="file" accept="{{ this.fileType }}" style="display: none">
|
||||
</div>
|
||||
</label>
|
||||
</div>
|
||||
@@ -37,6 +36,7 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
|
||||
@Input() descriptor: IComponentDescriptor;
|
||||
@Input() modelStore: IModelStore;
|
||||
private _button: Button;
|
||||
private fileType: string = '.sql';
|
||||
|
||||
@ViewChild('input', { read: ElementRef }) private _inputContainer: ElementRef;
|
||||
@ViewChild('fileInput', { read: ElementRef }) private _fileInputContainer: ElementRef;
|
||||
@@ -71,7 +71,10 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
|
||||
self.fileContent = text.toString();
|
||||
self.fireEvent({
|
||||
eventType: ComponentEventType.onDidClick,
|
||||
args: self.fileContent
|
||||
args: {
|
||||
filePath: file.path,
|
||||
fileContent: self.fileContent
|
||||
}
|
||||
});
|
||||
};
|
||||
reader.readAsText(file);
|
||||
@@ -101,6 +104,9 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
|
||||
super.setProperties(properties);
|
||||
this._button.enabled = this.enabled;
|
||||
this._button.label = this.label;
|
||||
if (this.properties.fileType) {
|
||||
this.fileType = properties.fileType;
|
||||
}
|
||||
this._button.title = this.title;
|
||||
|
||||
// Button's ariaLabel gets set to the label by default.
|
||||
@@ -116,6 +122,7 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
|
||||
this._button.setWidth(this.convertSize(this.height.toString()));
|
||||
}
|
||||
this.updateIcon();
|
||||
this._changeRef.detectChanges();
|
||||
}
|
||||
|
||||
protected updateIcon() {
|
||||
@@ -182,6 +189,10 @@ export default class ButtonComponent extends ComponentWithIconBase implements IC
|
||||
this.setPropertyFromUI<azdata.ButtonProperties, string>((properties, title) => { properties.title = title; }, newValue);
|
||||
}
|
||||
|
||||
private setFileType(value: string) {
|
||||
this.properties.fileType = value;
|
||||
}
|
||||
|
||||
private get ariaLabel(): string {
|
||||
return this.getPropertyOrDefault<azdata.ButtonProperties, string>((properties) => properties.ariaLabel, '');
|
||||
}
|
||||
|
||||
@@ -56,6 +56,8 @@ import { AlertsViewComponent } from 'sql/workbench/parts/jobManagement/browser/a
|
||||
import { JobHistoryComponent } from 'sql/workbench/parts/jobManagement/browser/jobHistory.component';
|
||||
import { OperatorsViewComponent } from 'sql/workbench/parts/jobManagement/browser/operatorsView.component';
|
||||
import { ProxiesViewComponent } from 'sql/workbench/parts/jobManagement/browser/proxiesView.component';
|
||||
import { NotebooksViewComponent } from 'sql/workbench/parts/jobManagement/browser/notebooksView.component';
|
||||
import { NotebookHistoryComponent } from 'sql/workbench/parts/jobManagement/browser/notebookHistory.component';
|
||||
import LoadingSpinner from 'sql/workbench/browser/modelComponents/loadingSpinner.component';
|
||||
import { Checkbox } from 'sql/base/browser/ui/checkbox/checkbox.component';
|
||||
import { SelectBox } from 'sql/platform/browser/selectBox/selectBox.component';
|
||||
@@ -65,7 +67,7 @@ import { EditableDropDown } from 'sql/platform/browser/editableDropdown/editable
|
||||
const baseComponents = [DashboardHomeContainer, DashboardComponent, DashboardWidgetWrapper, DashboardWebviewContainer,
|
||||
DashboardWidgetContainer, DashboardGridContainer, DashboardErrorContainer, DashboardNavSection, ModelViewContent, WebviewContent, WidgetContent,
|
||||
ComponentHostDirective, BreadcrumbComponent, ControlHostContent, DashboardControlHostContainer,
|
||||
JobsViewComponent, AgentViewComponent, JobHistoryComponent, JobStepsViewComponent, AlertsViewComponent, ProxiesViewComponent, OperatorsViewComponent,
|
||||
JobsViewComponent, NotebooksViewComponent, AgentViewComponent, JobHistoryComponent, NotebookHistoryComponent, JobStepsViewComponent, AlertsViewComponent, ProxiesViewComponent, OperatorsViewComponent,
|
||||
DashboardModelViewContainer, ModelComponentWrapper, Checkbox, EditableDropDown, SelectBox, InputBox, LoadingSpinner];
|
||||
|
||||
/* Panel */
|
||||
@@ -89,6 +91,7 @@ import { JobStepsViewComponent } from 'sql/workbench/parts/jobManagement/browser
|
||||
import { IInstantiationService, _util } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { ILogService } from 'vs/platform/log/common/log';
|
||||
|
||||
|
||||
const widgetComponents = [
|
||||
PropertiesWidgetComponent,
|
||||
ExplorerWidget,
|
||||
|
||||
@@ -17,6 +17,17 @@
|
||||
</div>
|
||||
</ng-template>
|
||||
</tab>
|
||||
<tab [title]="notebooksComponentTitle" class="fullsize" [identifier]="notebooksTabIdentifier"
|
||||
[iconClass]="notebooksIconClass">
|
||||
<ng-template>
|
||||
<div id="notebooksDiv" class="fullsize" *ngIf="showNotebookHistory === false">
|
||||
<notebooksview-component></notebooksview-component>
|
||||
</div>
|
||||
<div id="historyDiv" class="fullsize" *ngIf="showNotebookHistory === true">
|
||||
<notebookhistory-component></notebookhistory-component>
|
||||
</div>
|
||||
</ng-template>
|
||||
</tab>
|
||||
<tab [title]="alertsComponentTitle" class="fullsize" [identifier]="alertsTabIdentifier"
|
||||
[iconClass]="alertsIconClass">
|
||||
<ng-template>
|
||||
|
||||
@@ -7,7 +7,7 @@ import 'vs/css!./media/jobs';
|
||||
|
||||
import * as nls from 'vs/nls';
|
||||
import { Component, Inject, forwardRef, ChangeDetectorRef, ViewChild, Injectable } from '@angular/core';
|
||||
import { AgentJobInfo } from 'azdata';
|
||||
import { AgentJobInfo, AgentNotebookInfo } from 'azdata';
|
||||
import { PanelComponent, IPanelOptions, NavigationBarLayout } from 'sql/base/browser/ui/panel/panel.component';
|
||||
import { IJobManagementService } from 'sql/platform/jobManagement/common/interfaces';
|
||||
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
|
||||
@@ -25,17 +25,23 @@ export class AgentViewComponent {
|
||||
@ViewChild(PanelComponent) private _panel: PanelComponent;
|
||||
|
||||
private _showHistory: boolean = false;
|
||||
private _showNotebookHistory: boolean = false;
|
||||
private _jobId: string = null;
|
||||
private _notebookId: string = null;
|
||||
private _agentJobInfo: AgentJobInfo = null;
|
||||
private _agentNotebookInfo: AgentNotebookInfo = null;
|
||||
private _refresh: boolean = undefined;
|
||||
private _expanded: Map<string, string>;
|
||||
private _expandedNotebook: Map<string, string>;
|
||||
|
||||
public jobsIconClass: string = 'jobsview-icon';
|
||||
public notebooksIconClass: string = 'notebooksview-icon';
|
||||
public alertsIconClass: string = 'alertsview-icon';
|
||||
public proxiesIconClass: string = 'proxiesview-icon';
|
||||
public operatorsIconClass: string = 'operatorsview-icon';
|
||||
|
||||
private readonly jobsComponentTitle: string = nls.localize('jobview.Jobs', "Jobs");
|
||||
private readonly notebooksComponentTitle: string = nls.localize('jobview.Notebooks', "Notebooks");
|
||||
private readonly alertsComponentTitle: string = nls.localize('jobview.Alerts', "Alerts");
|
||||
private readonly proxiesComponentTitle: string = nls.localize('jobview.Proxies', "Proxies");
|
||||
private readonly operatorsComponentTitle: string = nls.localize('jobview.Operators', "Operators");
|
||||
@@ -67,14 +73,26 @@ export class AgentViewComponent {
|
||||
return this._jobId;
|
||||
}
|
||||
|
||||
public get notebookId(): string {
|
||||
return this._notebookId;
|
||||
}
|
||||
|
||||
public get showHistory(): boolean {
|
||||
return this._showHistory;
|
||||
}
|
||||
|
||||
public get showNotebookHistory(): boolean {
|
||||
return this._showNotebookHistory;
|
||||
}
|
||||
|
||||
public get agentJobInfo(): AgentJobInfo {
|
||||
return this._agentJobInfo;
|
||||
}
|
||||
|
||||
public get agentNotebookInfo(): AgentNotebookInfo {
|
||||
return this._agentNotebookInfo;
|
||||
}
|
||||
|
||||
public get refresh(): boolean {
|
||||
return this._refresh;
|
||||
}
|
||||
@@ -83,6 +101,10 @@ export class AgentViewComponent {
|
||||
return this._expanded;
|
||||
}
|
||||
|
||||
public get expandedNotebook(): Map<string, string> {
|
||||
return this._expandedNotebook;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public Setters
|
||||
*/
|
||||
@@ -91,15 +113,28 @@ export class AgentViewComponent {
|
||||
this._jobId = value;
|
||||
}
|
||||
|
||||
public set notebookId(value: string) {
|
||||
this._notebookId = value;
|
||||
}
|
||||
|
||||
public set showHistory(value: boolean) {
|
||||
this._showHistory = value;
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
|
||||
public set showNotebookHistory(value: boolean) {
|
||||
this._showNotebookHistory = value;
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
|
||||
public set agentJobInfo(value: AgentJobInfo) {
|
||||
this._agentJobInfo = value;
|
||||
}
|
||||
|
||||
public set agentNotebookInfo(value: AgentNotebookInfo) {
|
||||
this._agentNotebookInfo = value;
|
||||
}
|
||||
|
||||
public set refresh(value: boolean) {
|
||||
this._refresh = value;
|
||||
this._cd.detectChanges();
|
||||
@@ -109,10 +144,18 @@ export class AgentViewComponent {
|
||||
this._expanded.set(jobId, errorMessage);
|
||||
}
|
||||
|
||||
public setExpandedNotebook(jobId: string, errorMessage: string) {
|
||||
this._expandedNotebook.set(jobId, errorMessage);
|
||||
}
|
||||
|
||||
public set expanded(value: Map<string, string>) {
|
||||
this._expanded = value;
|
||||
}
|
||||
|
||||
public set expandedNotebook(value: Map<string, string>) {
|
||||
this._expandedNotebook = value;
|
||||
}
|
||||
|
||||
public layout() {
|
||||
this._panel.layout();
|
||||
}
|
||||
|
||||
@@ -130,3 +130,8 @@ export interface JobActionContext {
|
||||
canEdit: boolean;
|
||||
job: azdata.AgentJobInfo;
|
||||
}
|
||||
|
||||
export interface NotebookActionContext {
|
||||
canEdit: boolean;
|
||||
notebook: azdata.AgentNotebookInfo;
|
||||
}
|
||||
|
||||
@@ -507,11 +507,11 @@ export class JobsViewComponent extends JobManagementView implements OnInit, OnDe
|
||||
if (runChart && runChart.length > 0) {
|
||||
return `<table class="jobprevruns" id="${dataContext.id}">
|
||||
<tr>
|
||||
<td>${runChart[0] ? runChart[0] : '<div></div>'}</td>
|
||||
<td>${runChart[1] ? runChart[1] : '<div></div>'}</td>
|
||||
<td>${runChart[2] ? runChart[2] : '<div></div>'}</td>
|
||||
<td>${runChart[3] ? runChart[3] : '<div></div>'}</td>
|
||||
<td>${runChart[4] ? runChart[4] : '<div></div>'}</td>
|
||||
<td>${runChart[0] ? runChart[0] : '<div class="bar0"></div>'}</td>
|
||||
<td>${runChart[1] ? runChart[1] : '<div class="bar1"></div>'}</td>
|
||||
<td>${runChart[2] ? runChart[2] : '<div class="bar2"></div>'}</td>
|
||||
<td>${runChart[3] ? runChart[3] : '<div class="bar3"></div>'}</td>
|
||||
<td>${runChart[4] ? runChart[4] : '<div class="bar4"></div>'}</td>
|
||||
</tr>
|
||||
</table>`;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>Artboard 240</title>
|
||||
<g>
|
||||
<path d="M11.5,7,7,16h9Zm.5,8H11V14h1Zm-1-1.5V11h1v2.5Z" fill="#db7500"/>
|
||||
<path d="M5,6h6V3H5ZM6,4h4V5H6Z"/>
|
||||
<polygon points="5.882 16 6.382 15 2 15 2 13 3 13 3 12 2 12 2 10.023 3 10.023 3 9.023 2 9.023 2 7.039 3 7.039 3 6.039 2 6.039 2 4 3 4 3 3 2 3 2 1 13 1 13 7.764 14 9.764 14 0 1 0 1 3 0 3 0 4 1 4 1 6.039 0 6.039 0 7.039 1 7.039 1 9.023 0 9.023 0 10.023 1 10.023 1 12 0 12 0 13 1 13 1 16 5.882 16"/>
|
||||
<path d="M11,15h1V14H11Zm0-4v2.5h1V11Z" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 610 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>Artboard 210</title>
|
||||
<g>
|
||||
<path d="M11.5,7,7,16h9Zm.5,8H11V14h1Zm-1-1.5V11h1v2.5Z" fill="#db7500"/>
|
||||
<path d="M5,6h6V3H5ZM6,4h4V5H6Z" fill="#fff"/>
|
||||
<polygon points="5.882 16 6.382 15 2 15 2 13 3 13 3 12 2 12 2 10.023 3 10.023 3 9.023 2 9.023 2 7.039 3 7.039 3 6.039 2 6.039 2 4 3 4 3 3 2 3 2 1 13 1 13 7.764 14 9.764 14 0 1 0 1 3 0 3 0 4 1 4 1 6.039 0 6.039 0 7.039 1 7.039 1 9.023 0 9.023 0 10.023 1 10.023 1 12 0 12 0 13 1 13 1 16 5.882 16" fill="#fff"/>
|
||||
<path d="M11,15h1V14H11Zm0-4v2.5h1V11Z" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 634 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>Artboard 260</title>
|
||||
<g>
|
||||
<path d="M15.645,9.75A4.528,4.528,0,0,0,13.25,7.356a4.479,4.479,0,0,0-3.5,0A4.528,4.528,0,0,0,7.355,9.75a4.491,4.491,0,0,0,0,3.5A4.526,4.526,0,0,0,9.75,15.645a4.491,4.491,0,0,0,3.5,0,4.526,4.526,0,0,0,2.395-2.395,4.491,4.491,0,0,0,0-3.5Z" fill="#e00b1c"/>
|
||||
<path d="M5,6h6V3H5ZM6,4h4V5H6Z"/>
|
||||
<path d="M1,0V3H0V4H1V6.039H0v1H1V9.023H0v1H1V12H0v1H1v3H7.938a6.581,6.581,0,0,1-.524-.464A3.812,3.812,0,0,1,6.961,15H2V13H3V12H2V10.023H3v-1H2V7.039H3v-1H2V4H3V3H2V1H13V5.961a4.87,4.87,0,0,1,1,.375V0Z"/>
|
||||
<polygon points="12.648 9.649 11.5 10.797 10.352 9.649 9.648 10.352 10.797 11.5 9.648 12.649 10.352 13.352 11.5 12.204 12.648 13.352 13.352 12.649 12.203 11.5 13.352 10.352 12.648 9.649" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 847 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>Artboard 230</title>
|
||||
<g>
|
||||
<path d="M15.645,9.75A4.528,4.528,0,0,0,13.25,7.356a4.479,4.479,0,0,0-3.5,0A4.528,4.528,0,0,0,7.355,9.75a4.491,4.491,0,0,0,0,3.5A4.526,4.526,0,0,0,9.75,15.645a4.491,4.491,0,0,0,3.5,0,4.526,4.526,0,0,0,2.395-2.395,4.491,4.491,0,0,0,0-3.5Z" fill="#e00b1c"/>
|
||||
<path d="M5,6h6V3H5ZM6,4h4V5H6Z" fill="#fff"/>
|
||||
<path d="M1,0V3H0V4H1V6.039H0v1H1V9.023H0v1H1V12H0v1H1v3H7.938a6.581,6.581,0,0,1-.524-.464A3.812,3.812,0,0,1,6.961,15H2V13H3V12H2V10.023H3v-1H2V7.039H3v-1H2V4H3V3H2V1H13V5.961a4.87,4.87,0,0,1,1,.375V0Z" fill="#fff"/>
|
||||
<polygon points="12.648 9.649 11.5 10.797 10.352 9.649 9.648 10.352 10.797 11.5 9.648 12.649 10.352 13.352 11.5 12.204 12.648 13.352 13.352 12.649 12.203 11.5 13.352 10.352 12.648 9.649" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 871 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>Artboard 250</title>
|
||||
<g>
|
||||
<path d="M15.645,9.75A4.528,4.528,0,0,0,13.25,7.356a4.479,4.479,0,0,0-3.5,0A4.528,4.528,0,0,0,7.355,9.75a4.491,4.491,0,0,0,0,3.5A4.526,4.526,0,0,0,9.75,15.645a4.491,4.491,0,0,0,3.5,0,4.526,4.526,0,0,0,2.395-2.395,4.491,4.491,0,0,0,0-3.5Z" fill="#57a300"/>
|
||||
<path d="M5,6h6V3H5ZM6,4h4V5H6Z"/>
|
||||
<path d="M1,0V3H0V4H1V6.039H0v1H1V9.023H0v1H1V12H0v1H1v3H7.938a6.581,6.581,0,0,1-.524-.464A3.812,3.812,0,0,1,6.961,15H2V13H3V12H2V10.023H3v-1H2V7.039H3v-1H2V4H3V3H2V1H13V5.961a4.87,4.87,0,0,1,1,.375V0Z"/>
|
||||
<path d="M13.224,9.794l.7.7-3,3L9.073,11.648l.7-.7,1.148,1.149Z" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 725 B |
@@ -0,0 +1,9 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 16 16">
|
||||
<title>Artboard 220</title>
|
||||
<g>
|
||||
<path d="M15.645,9.75A4.528,4.528,0,0,0,13.25,7.356a4.479,4.479,0,0,0-3.5,0A4.528,4.528,0,0,0,7.355,9.75a4.491,4.491,0,0,0,0,3.5A4.526,4.526,0,0,0,9.75,15.645a4.491,4.491,0,0,0,3.5,0,4.526,4.526,0,0,0,2.395-2.395,4.491,4.491,0,0,0,0-3.5Z" fill="#57a300"/>
|
||||
<path d="M5,6h6V3H5ZM6,4h4V5H6Z" fill="#fff"/>
|
||||
<path d="M1,0V3H0V4H1V6.039H0v1H1V9.023H0v1H1V12H0v1H1v3H7.938a6.581,6.581,0,0,1-.524-.464A3.812,3.812,0,0,1,6.961,15H2V13H3V12H2V10.023H3v-1H2V7.039H3v-1H2V4H3V3H2V1H13V5.961a4.87,4.87,0,0,1,1,.375V0Z" fill="#fff"/>
|
||||
<path d="M13.224,9.794l.7.7-3,3L9.073,11.648l.7-.7,1.148,1.149Z" fill="#fff"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 749 B |
@@ -3,15 +3,22 @@
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
|
||||
jobhistory-component .all-jobs {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
width: 10%;
|
||||
margin-top: 10px;
|
||||
}
|
||||
notebookhistory-component .all-jobs {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
font-size: 15px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.overview-container .overview-tab .resultsViewCollapsible {
|
||||
.overview-container .overview-tab .resultsViewCollapsible,
|
||||
.overview-container .overview-tab .notebooksgridViewCollapsible {
|
||||
padding: 15px;
|
||||
display: inline;
|
||||
}
|
||||
@@ -26,6 +33,11 @@ jobhistory-component .all-jobs {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
.history-container {
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.vs-dark .overview-container .overview-tab {
|
||||
color: #fff;
|
||||
}
|
||||
@@ -42,7 +54,13 @@ jobhistory-component .all-jobs {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
input#accordion {
|
||||
input#accordion{
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
.grid-arrow{
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
z-index: -1;
|
||||
@@ -72,7 +90,8 @@ input#accordion {
|
||||
background: #333333;
|
||||
}
|
||||
|
||||
.hc-black .overview-tab .accordion-content {
|
||||
.hc-black .overview-tab .accordion-content,
|
||||
.grid-arrow {
|
||||
background: #000000;
|
||||
border: 1px solid #2b56f2;
|
||||
}
|
||||
@@ -81,9 +100,9 @@ input#accordion {
|
||||
max-height: 0;
|
||||
overflow: hidden;
|
||||
background: #eaeaea;
|
||||
-webkit-transition: max-height .35s;
|
||||
-o-transition: max-height .35s;
|
||||
transition: max-height .35s;
|
||||
-webkit-transition: max-height 0.35s;
|
||||
-o-transition: max-height 0.35s;
|
||||
transition: max-height 0.35s;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -92,7 +111,13 @@ input#accordion {
|
||||
}
|
||||
|
||||
/* :checked */
|
||||
input#accordion:checked ~ .accordion-content {
|
||||
input#accordion:checked ~ .accordion-content,
|
||||
.grid-arrow:checked {
|
||||
max-height: 10em;
|
||||
}
|
||||
|
||||
input#accordion:checked ~ .accordion-content,
|
||||
.grid-arrow:checked {
|
||||
max-height: 10em;
|
||||
}
|
||||
|
||||
@@ -106,13 +131,13 @@ input#accordion:checked ~ .accordion-content {
|
||||
height: 3em;
|
||||
line-height: 3;
|
||||
text-align: center;
|
||||
-webkit-transition: all .3s;
|
||||
-o-transition: all .3s;
|
||||
transition: all .3s;
|
||||
-webkit-transition: all 0.3s;
|
||||
-o-transition: all 0.3s;
|
||||
transition: all 0.3s;
|
||||
}
|
||||
|
||||
.all-jobs > .back-button-icon {
|
||||
content: url('back.svg');
|
||||
content: url("back.svg");
|
||||
width: 20px;
|
||||
margin-right: 10px;
|
||||
float: left;
|
||||
@@ -121,30 +146,43 @@ input#accordion:checked ~ .accordion-content {
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.vs-dark .all-jobs >.back-button-icon,
|
||||
.hc-black .all-jobs >.back-button-icon {
|
||||
content: url('back_inverse.svg');
|
||||
.vs-dark .all-jobs > .back-button-icon,
|
||||
.hc-black .all-jobs > .back-button-icon {
|
||||
content: url("back_inverse.svg");
|
||||
}
|
||||
|
||||
.vs .action-label.icon.newStepIcon {
|
||||
background-image: url('new.svg');
|
||||
background-image: url("new.svg");
|
||||
}
|
||||
|
||||
.vs-dark .action-label.icon.newStepIcon,
|
||||
.hc-black .action-label.icon.newStepIcon {
|
||||
background-image: url('new_inverse.svg');
|
||||
background-image: url("new_inverse.svg");
|
||||
}
|
||||
|
||||
.vs .action-label.icon.opennotebook {
|
||||
background-image: url("open_notebook.svg");
|
||||
}
|
||||
|
||||
.vs-dark .action-label.icon.opennotebook,
|
||||
.hc-black .action-label.icon.newStepIcon {
|
||||
background-image: url("open_notebook_inverse.svg");
|
||||
}
|
||||
|
||||
jobhistory-component .hc-black .icon.edit,
|
||||
jobhistory-component .vs-dark .icon.edit {
|
||||
background-image: url('edit_inverse.svg');
|
||||
jobhistory-component .vs-dark .icon.edit,
|
||||
notebookhistory-component .hc-black .icon.edit,
|
||||
notebookhistory-component .vs-dark .icon.edit {
|
||||
background-image: url("edit_inverse.svg");
|
||||
}
|
||||
|
||||
jobhistory-component .vs .icon.edit {
|
||||
background-image: url('edit.svg');
|
||||
jobhistory-component .vs .icon.edit,
|
||||
notebookhistory-component .vs .icon.edit {
|
||||
background-image: url("edit.svg");
|
||||
}
|
||||
|
||||
jobhistory-component .actions-container .icon.edit {
|
||||
jobhistory-component .actions-container .icon.edit,
|
||||
notebookhistory-component .actions-container .icon.edit {
|
||||
background-position: 0% 50%;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 12px;
|
||||
@@ -210,12 +248,20 @@ table.step-list tr.step-row td {
|
||||
max-height: 200px;
|
||||
}
|
||||
|
||||
.step-table .monaco-tree .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children > .content:before {
|
||||
.step-table
|
||||
.monaco-tree
|
||||
.monaco-tree-rows.show-twisties
|
||||
> .monaco-tree-row.has-children
|
||||
> .content:before {
|
||||
background: none;
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
.step-table .monaco-tree.focused .monaco-tree-rows.show-twisties > .monaco-tree-row.has-children.selected:not(.loading) > .content:before {
|
||||
.step-table
|
||||
.monaco-tree.focused
|
||||
.monaco-tree-rows.show-twisties
|
||||
> .monaco-tree-row.has-children.selected:not(.loading)
|
||||
> .content:before {
|
||||
background-image: none;
|
||||
}
|
||||
|
||||
@@ -258,40 +304,182 @@ table.step-list tr.step-row td {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
jobhistory-component {
|
||||
jobhistory-component,
|
||||
notebookhistory-component {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
jobhistory-component > .jobhistory-heading-container {
|
||||
jobhistory-component > .jobhistory-heading-container,
|
||||
notebookhistory-component > .jobhistory-heading-container {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
jobhistory-component > .jobhistory-heading-container > .icon.in-progress {
|
||||
jobhistory-component > .jobhistory-heading-container > .icon.in-progress,
|
||||
notebookhistory-component > .jobhistory-heading-container > .icon.in-progress {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
padding-top: 16px;
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
jobhistory-component > .agent-actionbar-container {
|
||||
jobhistory-component > .agent-actionbar-container,
|
||||
notebookhistory-component > .agent-actionbar-container {
|
||||
border-top: 3px solid #f4f4f4;
|
||||
}
|
||||
|
||||
.vs-dark jobhistory-component > .agent-actionbar-container {
|
||||
.vs-dark jobhistory-component > .agent-actionbar-container,
|
||||
.vs-dark notebookhistory-component > .agent-actionbar-container {
|
||||
border-top: 3px solid #444444;
|
||||
}
|
||||
|
||||
.hc-black jobhistory-component > .agent-actionbar-container {
|
||||
.hc-black jobhistory-component > .agent-actionbar-container,
|
||||
.hc-black notebookhistory-component > .agent-actionbar-container {
|
||||
border-top: 3px solid #2b56f2;
|
||||
}
|
||||
|
||||
jobhistory-component .step-table.prev-run-list .monaco-tree-wrapper .monaco-tree-row {
|
||||
jobhistory-component
|
||||
.step-table.prev-run-list
|
||||
.monaco-tree-wrapper
|
||||
.monaco-tree-row,
|
||||
notebookhistory-component
|
||||
.step-table.prev-run-list
|
||||
.monaco-tree-wrapper
|
||||
.monaco-tree-row {
|
||||
width: 96%;
|
||||
}
|
||||
|
||||
jobhistory-component .agent-actionbar-container > .monaco-toolbar.carbon-taskbar {
|
||||
jobhistory-component
|
||||
.agent-actionbar-container
|
||||
> .monaco-toolbar.carbon-taskbar,
|
||||
notebookhistory-component
|
||||
.agent-actionbar-container
|
||||
> .monaco-toolbar.carbon-taskbar {
|
||||
margin: 10px 0px 5px 0px;
|
||||
}
|
||||
.notebook-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
||||
grid-gap: 10px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.notebook-grid-item {
|
||||
border-radius: 5px;
|
||||
padding-bottom: 5px;
|
||||
height: 95px;
|
||||
display: block;
|
||||
border-radius: 0px;
|
||||
}
|
||||
|
||||
.notebook-grid-item > img {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 10px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.notebook-grid-item:hover {
|
||||
background: #dcdcdc !important;
|
||||
}
|
||||
|
||||
.vs-dark .notebook-grid-item:hover,
|
||||
.hc-black .notebook-grid-item:hover {
|
||||
background: #444444 !important;
|
||||
}
|
||||
|
||||
.notebook-grid-item > .img-success {
|
||||
background-image: url(./NotebookSuccess_16x.svg);
|
||||
background-position: center;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 10px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-size: 40px 40px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .notebook-grid-item > .img-success,
|
||||
.hc-black .notebook-grid-item > .img-success {
|
||||
background-image: url(./NotebookSuccess_16x_white.svg);
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.notebook-grid-item > .img-failure {
|
||||
background-image: url(./NotebookFail_16x.svg);
|
||||
background-position: center;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 10px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-size: 40px 40px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .notebook-grid-item > .img-failure,
|
||||
.hc-black .notebook-grid-item > .img-failure {
|
||||
background-image: url(./NotebookFail_16x_white.svg);
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.notebook-grid-item > .img-error {
|
||||
background-image: url(./NotebookError_16x.svg);
|
||||
background-position: center;
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 10px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
background-size: 40px 40px;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.vs-dark .notebook-grid-item > .img-error,
|
||||
.hc-black .notebook-grid-item > .img-error {
|
||||
background-image: url(./NotebookError_16x_white.svg);
|
||||
background-position: center;
|
||||
}
|
||||
|
||||
.notebook-grid-item > p {
|
||||
display: absolute;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
margin-top: 10px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.notebook-history-container {
|
||||
flex: 1 1 auto;
|
||||
position: relative;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.grid-title {
|
||||
position: relative;
|
||||
display: block;
|
||||
padding: 0 0 0 1em;
|
||||
background: #f4f4f4;
|
||||
font-weight: bold;
|
||||
line-height: 3;
|
||||
cursor: pointer;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.vs-dark .grid-title {
|
||||
background: #333333;
|
||||
}
|
||||
|
||||
.hc-black .grid-title {
|
||||
background: #000000;
|
||||
border: 1px solid #2b56f2;
|
||||
}
|
||||
|
||||
@@ -9,12 +9,14 @@ agentview-component {
|
||||
display: block;
|
||||
}
|
||||
|
||||
jobsview-component {
|
||||
jobsview-component,
|
||||
notebooksview-component {
|
||||
height: 100%;
|
||||
width : 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
.job-heading-container {
|
||||
height: 50px;
|
||||
border-bottom: 3px solid #f4f4f4;
|
||||
@@ -35,6 +37,12 @@ jobsview-component {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.jobnotebooksview-grid {
|
||||
height: calc(100% - 75px);
|
||||
width : 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.vs-dark #agentViewDiv .slick-header-column {
|
||||
background: #333333 !important;
|
||||
}
|
||||
@@ -45,52 +53,63 @@ jobsview-component {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hc-black #jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
|
||||
.hc-black #jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell,
|
||||
.hc-black #notebooksDiv notebooks-component .jobnotebooksview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
|
||||
border: 1px solid #2b56f2;
|
||||
}
|
||||
|
||||
#jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
|
||||
#jobsDiv jobsview-component .jobview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell,
|
||||
#notebooksDiv notebooksview-component .jobnotebooksview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
|
||||
border-right: transparent !important;
|
||||
border-left: transparent !important;
|
||||
line-height: 33px !important;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-joblist {
|
||||
#jobsDiv .jobview-joblist,
|
||||
#notebooksDiv .jobview-joblist {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-jobnametable {
|
||||
#jobsDiv .jobview-jobnametable,
|
||||
#notebooksDiv .jobview-jobnametable {
|
||||
border: 0px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-jobnameindicatorsuccess {
|
||||
#jobsDiv .jobview-jobnameindicatorsuccess,
|
||||
#notebooksDiv .jobview-jobnameindicatorsuccess {
|
||||
width: 5px;
|
||||
background: green;
|
||||
}
|
||||
|
||||
#jobsDiv .slick-cell.l1.r1 .jobview-jobnameindicatorfailure {
|
||||
#jobsDiv .slick-cell.l1.r1 .jobview-jobnameindicatorfailure,
|
||||
#notebooksDiv .slick-cell.l1.r1 .jobview-jobnameindicatorfailure {
|
||||
width: 5px;
|
||||
background: red;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-jobnameindicatorcancel {
|
||||
#jobsDiv .jobview-jobnameindicatorcancel,
|
||||
#notebooksDiv .jobview-jobnameindicatorcancel {
|
||||
width: 5px;
|
||||
background: orange;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-grid .jobview-jobnameindicatorunknown {
|
||||
#jobsDiv .jobview-grid .jobview-jobnameindicatorunknown,
|
||||
#notebooks .jobnotebooksview-grid .jobview-jobnameindicatorunknown {
|
||||
width: 5px;
|
||||
background: grey;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-grid .slick-cell.l1.r1.error-row .jobview-jobnametext {
|
||||
#jobsDiv .jobview-grid .slick-cell.l1.r1.error-row .jobview-jobnametext,
|
||||
#notebooksDiv .jobnotebooksview-grid .slick-cell.l1.r1.error-row .jobview-jobnametext,
|
||||
#notebooksDiv .jobnotebooksview-grid .slick-cell.l1.r1.notebook-error-row .jobview-jobnametext {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-grid .slick-cell.l1.r1 .jobview-jobnametext {
|
||||
#jobsDiv .jobview-grid .slick-cell.l1.r1 .jobview-jobnametext,
|
||||
#notebooksDiv .jobnotebooksview-grid .slick-cell.l1.r1 .jobview-jobnametext {
|
||||
text-overflow: ellipsis;
|
||||
width: 250px;
|
||||
overflow: hidden;
|
||||
@@ -108,28 +127,44 @@ jobsview-component {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#jobsDiv .job-with-error {
|
||||
#jobsDiv .job-with-error,
|
||||
#notebooksDiv .job-with-error {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell.l1.r1.error-row {
|
||||
.jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell.l1.r1.error-row,
|
||||
.jobnotebooksview-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;
|
||||
text-overflow: ellipsis;
|
||||
color: orangered;
|
||||
}
|
||||
|
||||
.jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell._detail_selector.error-row {
|
||||
.jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell.l1.r1.notebook-error-row {
|
||||
width: 100%;
|
||||
opacity: 1;
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: orange;
|
||||
}
|
||||
|
||||
.jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell._detail_selector.error-row,
|
||||
.jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell._detail_selector.error-row,
|
||||
.jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row .slick-cell._detail_selector.notebook-error-row {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-splitter {
|
||||
#jobsDiv .jobview-splitter,
|
||||
#notebooksDiv .jobview-splitter {
|
||||
height: 1px;
|
||||
width: 100%;
|
||||
background-color: gray;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-jobitem {
|
||||
#jobsDiv .jobview-jobitem,
|
||||
#notebooksDiv .jobview-jobitem {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
@@ -137,30 +172,36 @@ jobsview-component {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-label {
|
||||
#jobsDiv .jobview-label,
|
||||
#notebooksDiv .jobview-label {
|
||||
padding-bottom: 10px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-highlight-none {
|
||||
#jobsDiv .jobview-highlight-none,
|
||||
#notebooksDiv .jobview-highlight-none {
|
||||
width: 5px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
#jobsDiv .detail-container {
|
||||
#jobsDiv .detail-container,
|
||||
#notebooksDiv .detail-container {
|
||||
max-height: 100px !important;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
#jobsDiv .detail {
|
||||
#jobsDiv .detail,
|
||||
#notebooksDiv .detail {
|
||||
padding: 5px
|
||||
}
|
||||
|
||||
#jobsDiv .preload {
|
||||
#jobsDiv .preload,
|
||||
#notebooksDiv .preload {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
#jobsDiv .dynamic-cell-detail > :first-child {
|
||||
#jobsDiv .dynamic-cell-detail > :first-child,
|
||||
#notebooksDiv .dynamic-cell-detail > :first-child {
|
||||
vertical-align: middle;
|
||||
line-height: 13px;
|
||||
padding: 10px;
|
||||
@@ -171,11 +212,22 @@ jobsview-component {
|
||||
background-image: url('./job.svg');
|
||||
}
|
||||
|
||||
|
||||
.vs-dark .jobsview-icon,
|
||||
.hc-black .jobsview-icon {
|
||||
background-image: url('./job_inverse.svg');
|
||||
}
|
||||
|
||||
|
||||
.notebooksview-icon {
|
||||
background-image: url('./notebook.svg');
|
||||
}
|
||||
|
||||
.vs-dark .notebooksview-icon,
|
||||
.hc-black .notebooksview-icon {
|
||||
background-image: url('./notebook_inverse.svg');
|
||||
}
|
||||
|
||||
.alertsview-icon {
|
||||
background-image: url('./alert.svg');
|
||||
}
|
||||
@@ -204,7 +256,7 @@ jobsview-component {
|
||||
}
|
||||
|
||||
agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.even > .slick-cell,
|
||||
agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.odd > .slick-cell {
|
||||
agentview-component .jobnotebooksview-grid .grid-canvas > .ui-widget-content.slick-row.odd > .slick-cell {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
@@ -227,26 +279,37 @@ agentview-component .jobview-grid .grid-canvas > .ui-widget-content.slick-row.od
|
||||
padding-left: 15px;
|
||||
}
|
||||
|
||||
#jobsDiv jobsview-component .jobview-grid .slick-cell.l1.r1.error-row td.jobview-jobnameindicatorfailure {
|
||||
#jobsDiv jobsview-component .jobview-grid .slick-cell.l1.r1.error-row td.jobview-jobnameindicatorfailure,
|
||||
#notebooksDiv notebooksview-component .jobnotebooksview-grid .slick-cell.l1.r1.error-row td.jobview-jobnameindicatorfailure,
|
||||
#notebooksDiv notebooksview-component .jobnotebooksview-grid .slick-cell.l1.r1.notebook-error-row td.jobview-jobnameindicatorfailure {
|
||||
width: 0;
|
||||
background: none;
|
||||
}
|
||||
|
||||
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
|
||||
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
|
||||
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered {
|
||||
#jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered,
|
||||
#notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
|
||||
#notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
|
||||
#notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered {
|
||||
background: #dcdcdc !important;
|
||||
}
|
||||
|
||||
.vs-dark #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
|
||||
.vs-dark #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
|
||||
.vs-dark #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
|
||||
.vs-dark #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
|
||||
.vs-dark #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
|
||||
.vs-dark #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
|
||||
.vs-dark #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
|
||||
background: #444444 !important;
|
||||
}
|
||||
|
||||
.hc-black #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
|
||||
.hc-black #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
|
||||
.hc-black #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
|
||||
.hc-black #jobsDiv .jobview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
|
||||
.hc-black #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
|
||||
.hc-black #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
|
||||
.hc-black #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
@@ -257,7 +320,8 @@ table.jobprevruns div.bar3, table.jobprevruns div.bar4, table.jobprevruns div.ba
|
||||
width: 10px;
|
||||
}
|
||||
|
||||
.jobview-grid .slick-cell.l10.r10 {
|
||||
.jobview-grid .slick-cell.l10.r10,
|
||||
.jobnotebooksview-grid .slick-cell.l10.r10 {
|
||||
text-align: center;
|
||||
display: inline-flex;
|
||||
}
|
||||
@@ -270,6 +334,12 @@ table.jobprevruns > tbody {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
#notebooksDiv .jobnotebooksview-grid {
|
||||
height: calc(100% - 75px);
|
||||
width : 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#alertsDiv .jobalertsview-grid {
|
||||
height: calc(100% - 75px);
|
||||
width : 100%;
|
||||
@@ -289,6 +359,8 @@ table.jobprevruns > tbody {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.vs .action-label.icon.refreshIcon {
|
||||
background-image: url('refresh.svg');
|
||||
}
|
||||
@@ -298,14 +370,52 @@ table.jobprevruns > tbody {
|
||||
background-image: url('refresh_inverse.svg');
|
||||
}
|
||||
|
||||
|
||||
.vs .action-label.icon.openNotebook {
|
||||
background-image: url('open_notebook.svg');
|
||||
}
|
||||
|
||||
.vs-dark .action-label.icon.openNotebook,
|
||||
.hc-black .action-label.icon.openNotebook {
|
||||
background-image: url('open_notebook_inverse.svg');
|
||||
}
|
||||
|
||||
.agent-actionbar-container .monaco-action-bar > ul.actions-container > li.action-item {
|
||||
padding-left: 20px;
|
||||
}
|
||||
|
||||
jobsview-component .jobview-grid .slick-cell.error-row {
|
||||
jobsview-component .jobview-grid .slick-cell.error-row,
|
||||
notebooksview-component .jobnotebooksview-grid .slick-cell.error-row,
|
||||
notebooksview-component .jobnotebooksview-grid .slick-cell.notebook-error-row {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#notebooksDiv notebooksview-component .jobnotebooksview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
|
||||
border-right: transparent !important;
|
||||
border-left: transparent !important;
|
||||
line-height: 33px !important;
|
||||
}
|
||||
|
||||
#notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
|
||||
#notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell,
|
||||
#notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row> .slick-cell.hovered {
|
||||
background: #dcdcdc !important;
|
||||
}
|
||||
|
||||
.vs-dark #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row:hover > .slick-cell,
|
||||
.vs-dark #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row > .slick-cell.hovered,
|
||||
.vs-dark #notebooksDiv .jobnotebooksview-grid > .monaco-table .slick-viewport > .grid-canvas > .ui-widget-content.slick-row.hovered > .slick-cell {
|
||||
background: #444444 !important;
|
||||
}
|
||||
|
||||
.vs-dark .jobnotebooksview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
|
||||
border-left: 1px dotted white;
|
||||
}
|
||||
|
||||
.jobnotebooksview-grid > .monaco-table .slick-header-columns .slick-resizable-handle {
|
||||
border-left: 1px dotted #444444;
|
||||
}
|
||||
|
||||
#alertsDiv jobalertsview-component .jobalertsview-grid .grid-canvas .ui-widget-content.slick-row .slick-cell {
|
||||
border-right: transparent !important;
|
||||
border-left: transparent !important;
|
||||
@@ -403,8 +513,9 @@ jobsview-component .jobview-grid .slick-cell.error-row {
|
||||
}
|
||||
|
||||
#jobsDiv jobsview-component .monaco-toolbar.carbon-taskbar,
|
||||
#notebooksDiv notebooksview-component .monaco-toolbar.carbon-taskbar,
|
||||
#operatorsDiv joboperatorsview-component .monaco-toolbar.carbon-taskbar,
|
||||
#alertsDiv jobalertsview-component .monaco-toolbar.carbon-taskbar,
|
||||
#proxiesDiv jobproxiesview-component .monaco-toolbar.carbon-taskbar {
|
||||
margin: 10px 0px 10px 0px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><title>notebook</title><path d="M15.5,2V15H.5V2h2V1h3a4.19,4.19,0,0,1,1.32.21A3.87,3.87,0,0,1,8,1.84a3.87,3.87,0,0,1,1.18-.63A4.19,4.19,0,0,1,10.5,1h3V2ZM1.5,14H7.8a4.43,4.43,0,0,0-.51-.43,3.41,3.41,0,0,0-.54-.31,2.74,2.74,0,0,0-.59-.2A3.2,3.2,0,0,0,5.5,13h-3V3h-1Zm2-2h2a4.18,4.18,0,0,1,1,.13,4,4,0,0,1,1,.39V2.72a3,3,0,0,0-.94-.54A3.15,3.15,0,0,0,5.5,2h-2Zm11-9h-1V13h-3a3.2,3.2,0,0,0-.67.07,2.74,2.74,0,0,0-.59.2,3.41,3.41,0,0,0-.54.31A4.43,4.43,0,0,0,8.2,14h6.3Zm-4-1a3.15,3.15,0,0,0-1.06.18,3,3,0,0,0-.94.54v9.8a4,4,0,0,1,1-.39,4.18,4.18,0,0,1,1-.13h2V2Z"/></svg>
|
||||
|
After Width: | Height: | Size: 661 B |
@@ -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>notebook_inverse</title><path class="cls-1" d="M15.46,2V15H.46V2h2V1h3a4.19,4.19,0,0,1,1.32.21A3.87,3.87,0,0,1,8,1.84a3.87,3.87,0,0,1,1.18-.63A4.19,4.19,0,0,1,10.46,1h3V2Zm-14,12h6.3a4.43,4.43,0,0,0-.51-.43,3.41,3.41,0,0,0-.54-.31,2.74,2.74,0,0,0-.59-.2A3.2,3.2,0,0,0,5.46,13h-3V3h-1Zm2-2h2a4.18,4.18,0,0,1,1,.13,4,4,0,0,1,1,.39V2.72a3,3,0,0,0-.94-.54A3.15,3.15,0,0,0,5.46,2h-2Zm11-9h-1V13h-3a3.2,3.2,0,0,0-.67.07,2.74,2.74,0,0,0-.59.2,3.41,3.41,0,0,0-.54.31,4.43,4.43,0,0,0-.51.43h6.3Zm-4-1a3.15,3.15,0,0,0-1.06.18,3,3,0,0,0-.94.54v9.8a4,4,0,0,1,1-.39,4.18,4.18,0,0,1,1-.13h2V2Z"/></svg>
|
||||
|
After Width: | Height: | Size: 734 B |
@@ -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:#00539c;}</style></defs><title>open_notebook</title><path d="M12.4,4.21l-.08-.11h-.56l-.69.06a1.54,1.54,0,0,0-.23.29v8.69H9a3.32,3.32,0,0,0-.93.13,3.34,3.34,0,0,0-.87.34V4.76a2.88,2.88,0,0,1,.43-.31A5.58,5.58,0,0,1,8.14,3.3a2.63,2.63,0,0,0-.3.09A3.62,3.62,0,0,0,6.78,4a3.68,3.68,0,0,0-1.07-.57A3.58,3.58,0,0,0,4.52,3.2H1.81v.9H0V15.85H13.57V5.48ZM2.71,4.1H4.52a2.61,2.61,0,0,1,1,.17,2.32,2.32,0,0,1,.86.49v8.85a3.27,3.27,0,0,0-.88-.34,3.22,3.22,0,0,0-.93-.13H2.71ZM.9,15V5h.91v9H4.52a3.94,3.94,0,0,1,.61.06,3.2,3.2,0,0,1,.52.18,4.19,4.19,0,0,1,.49.29,2.28,2.28,0,0,1,.45.39Zm11.75,0H7a2.7,2.7,0,0,1,.47-.39,2.83,2.83,0,0,1,.47-.29,3.42,3.42,0,0,1,.54-.18A3.81,3.81,0,0,1,9,14h2.73V5h.89Z"/><polygon class="cls-1" points="13.05 3.56 13.05 3.58 13.04 3.57 13.05 3.56"/><path class="cls-1" d="M13,3.57h0v0Z"/><polygon class="cls-1" points="13.05 3.56 13.05 3.58 13.04 3.57 13.05 3.56"/><polygon class="cls-1" points="14.06 1.65 14.04 1.65 14.04 1.63 14.06 1.65"/><path class="cls-1" d="M15.76,2.1,14,3.81l-.38.38L13,3.58v0l1-1H12.64a3.35,3.35,0,0,0-1.09.26h0a3.94,3.94,0,0,0-.86.52l-.24.21s0,0,0,0a3.3,3.3,0,0,0-.51.67,3.1,3.1,0,0,0-.26.47,3.41,3.41,0,0,0-.27,1.39h-.9a4.68,4.68,0,0,1,.16-1.19,4.74,4.74,0,0,1,.25-.66,2.21,2.21,0,0,1,.2-.41,4.66,4.66,0,0,1,.36-.51c.1-.13.22-.26.34-.39a4.14,4.14,0,0,1,.66-.53,1.19,1.19,0,0,1,.23-.16A2.79,2.79,0,0,1,11,2.08l.31-.13.42-.14a4.32,4.32,0,0,1,1.19-.16h1.15l-1-1L13.67,0Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -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;}.cls-2{fill:#0095d7;}</style></defs><title>open_notebook_inverse</title><path class="cls-1" d="M12.55,4.21l-.08-.11h-.56l-.69.06a1.54,1.54,0,0,0-.23.29v8.69H9.18a3.32,3.32,0,0,0-.93.13,3.34,3.34,0,0,0-.87.34V4.76a2.88,2.88,0,0,1,.43-.31A5.58,5.58,0,0,1,8.29,3.3a2.63,2.63,0,0,0-.3.09A3.62,3.62,0,0,0,6.93,4a3.68,3.68,0,0,0-1.07-.57A3.58,3.58,0,0,0,4.67,3.2H2v.9H.15V15.85H13.72V5.48ZM2.86,4.1H4.67a2.61,2.61,0,0,1,1,.17,2.32,2.32,0,0,1,.86.49v8.85a3.27,3.27,0,0,0-.88-.34,3.22,3.22,0,0,0-.93-.13H2.86ZM1,15V5H2v9H4.67a3.94,3.94,0,0,1,.61.06,3.2,3.2,0,0,1,.52.18,4.19,4.19,0,0,1,.49.29,2.28,2.28,0,0,1,.45.39ZM12.8,15H7.11a2.7,2.7,0,0,1,.47-.39A2.83,2.83,0,0,1,8,14.28a3.42,3.42,0,0,1,.54-.18A3.81,3.81,0,0,1,9.18,14h2.73V5h.89Z"/><polygon class="cls-2" points="13.2 3.56 13.2 3.58 13.19 3.57 13.2 3.56"/><path class="cls-2" d="M13.19,3.57h0v0Z"/><polygon class="cls-2" points="13.2 3.56 13.2 3.58 13.19 3.57 13.2 3.56"/><polygon class="cls-2" points="14.21 1.65 14.19 1.65 14.19 1.63 14.21 1.65"/><path class="cls-2" d="M15.91,2.1,14.2,3.81l-.38.38-.62-.61v0l1-1H12.79a3.35,3.35,0,0,0-1.09.26h0a3.94,3.94,0,0,0-.86.52l-.24.21s0,0,0,0a3.3,3.3,0,0,0-.51.67,3.1,3.1,0,0,0-.26.47A3.41,3.41,0,0,0,9.5,6.11H8.6a4.68,4.68,0,0,1,.16-1.19A4.74,4.74,0,0,1,9,4.26a2.21,2.21,0,0,1,.2-.41,4.66,4.66,0,0,1,.36-.51c.1-.13.22-.26.34-.39a4.14,4.14,0,0,1,.66-.53,1.19,1.19,0,0,1,.23-.16,2.79,2.79,0,0,1,.34-.18l.31-.13.42-.14a4.32,4.32,0,0,1,1.19-.16h1.15l-1-1L13.82,0Z"/></svg>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
@@ -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:#d02e00;}.cls-2{fill:#fff;}</style></defs><title>error_16x16</title><circle class="cls-1" cx="8.07" cy="8.07" r="7.93"/><polygon class="cls-2" points="8.82 8.07 11.53 10.78 10.83 11.48 8.12 8.78 5.41 11.48 4.7 10.78 7.42 8.07 4.65 5.31 5.36 4.61 8.12 7.37 10.83 4.67 11.53 5.36 8.82 8.07"/></svg>
|
||||
|
After Width: | Height: | Size: 414 B |
@@ -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:#3bb44a;}</style></defs><title>success_16x16</title><path class="cls-1" d="M16,3.16,5.48,13.69,0,8.2l.89-.89,4.6,4.59,9.63-9.62Z"/></svg>
|
||||
|
After Width: | Height: | Size: 255 B |
@@ -0,0 +1,163 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div class="jobhistory-heading-container">
|
||||
<h1 class="job-heading">Notebook | {{ this._agentNotebookInfo?.name }}</h1>
|
||||
<div class="icon in-progress" *ngIf="showProgressWheel()"></div>
|
||||
</div>
|
||||
|
||||
<!-- Back -->
|
||||
<div
|
||||
class="all-jobs"
|
||||
tabindex="0"
|
||||
(click)="goToJobs()"
|
||||
(keyup.enter)="goToJobs()"
|
||||
>
|
||||
<div
|
||||
class="back-button-icon"
|
||||
(click)="goToJobs()"
|
||||
(keyup.enter)="goToJobs()"
|
||||
></div>
|
||||
All Notebooks
|
||||
</div>
|
||||
|
||||
<!-- Actions -->
|
||||
<div #actionbarContainer class="agent-actionbar-container"></div>
|
||||
|
||||
<!-- Overview -->
|
||||
<div class="overview-container">
|
||||
<div class="overview-tab" (click)="toggleCollapse()" tabindex="0">
|
||||
<input id="accordion" type="checkbox" />
|
||||
<label for="accordion">
|
||||
<div
|
||||
class="resultsViewCollapsible collapsed"
|
||||
(click)="toggleCollapse()"
|
||||
></div>
|
||||
Overview
|
||||
</label>
|
||||
<div class="accordion-content">
|
||||
<table align="left">
|
||||
<tr>
|
||||
<td id="col1">
|
||||
TargetDatabase:
|
||||
</td>
|
||||
<td id="col2">
|
||||
{{ this._agentNotebookInfo?.targetDatabase }}
|
||||
</td>
|
||||
<td id="col3">
|
||||
Enabled:
|
||||
</td>
|
||||
<td id="col4">
|
||||
{{ this._agentNotebookInfo?.enabled }}
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td id="col1">
|
||||
Last Run:
|
||||
</td>
|
||||
<td id="col2">
|
||||
{{ this._agentNotebookInfo?.lastRun }}
|
||||
</td>
|
||||
<td id="col3">
|
||||
Next Run:
|
||||
</td>
|
||||
<td id="col4">
|
||||
{{ this._agentNotebookInfo?.nextRun }}
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Overview -->
|
||||
<div class="history-container">
|
||||
<div
|
||||
class="overview-container"
|
||||
*ngFor="let grid of this._grids; let i = index"
|
||||
>
|
||||
<div
|
||||
class="overview-tab"
|
||||
(click)="toggleGridCollapse(i)"
|
||||
tabindex="{{i}}"
|
||||
*ngIf="grid.histories?.length"
|
||||
>
|
||||
<input id="accordion{{ i }}" type="checkbox" class="grid-arrow" />
|
||||
<label for="accordion{{ i }}">
|
||||
<div id= "history-grid-icon{{i}}" (click)="toggleGridCollapse(i)"
|
||||
class="resultsViewCollapsible"></div>
|
||||
{{ grid.title }}
|
||||
</label>
|
||||
<div id="notebook-grid{{ i }}" class="notebook-grid {{ i }}" >
|
||||
<div
|
||||
*ngFor="let history of grid.histories"
|
||||
class="notebook-grid-item"
|
||||
(dblclick)="openNotebook(history)"
|
||||
title="{{ createdTooltip(history) }}"
|
||||
(contextmenu)="
|
||||
openHistoryContextMenu($event, history, grid.contextMenuType)
|
||||
"
|
||||
>
|
||||
<div
|
||||
*ngIf="history.materializedNotebookErrorInfo"
|
||||
class="img-error"
|
||||
></div>
|
||||
<div
|
||||
*ngIf="
|
||||
history.runStatus === 1 && !history.materializedNotebookErrorInfo
|
||||
"
|
||||
class="img-success"
|
||||
></div>
|
||||
<div
|
||||
*ngIf="
|
||||
history.runStatus === 0 && !history.materializedNotebookErrorInfo
|
||||
"
|
||||
class="img-failure"
|
||||
></div>
|
||||
<div
|
||||
*ngIf="
|
||||
!history.materializedNotebookName || history.materializedNotebookName === '';
|
||||
else notebookRunName
|
||||
"
|
||||
>
|
||||
<p style="text-align: center;">
|
||||
{{ formatDateTimetoLocaleDate(history.runDate) }}
|
||||
<br />
|
||||
{{ formatDateTimetoLocaleTime(history.runDate) }}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
*ngIf="
|
||||
history.materializedNotebookName !== '';
|
||||
else notebookRunName
|
||||
"
|
||||
>
|
||||
<p
|
||||
style="text-align: center; text-overflow: ellipsis; overflow:hidden; white-space: nowrap"
|
||||
>
|
||||
{{ history.materializedNotebookName }}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Job History details -->
|
||||
<div class="history-details">
|
||||
<!-- Previous run list -->
|
||||
<div class="prev-run-list-container" style="min-width: 270px">
|
||||
<h3 *ngIf="_showPreviousRuns === false" style="text-align: center">
|
||||
No Previous Runs Available
|
||||
</h3>
|
||||
<div
|
||||
class="step-table prev-run-list"
|
||||
style="position: relative; width: 100%"
|
||||
>
|
||||
<div #table style="position: absolute; width: 100%; height: 100%"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -0,0 +1,608 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/jobHistory';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { OnInit, Component, Inject, Input, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, ChangeDetectionStrategy, Injectable, PipeTransform, Pipe } from '@angular/core';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
import { AgentViewComponent } from 'sql/workbench/parts/jobManagement/browser/agentView.component';
|
||||
import { CommonServiceInterface } from 'sql/platform/bootstrap/browser/commonServiceInterface.service';
|
||||
import { RunJobAction, StopJobAction, JobsRefreshAction, EditNotebookJobAction, EditJobAction, OpenMaterializedNotebookAction, OpenTemplateNotebookAction, RenameNotebookMaterializedAction, PinNotebookMaterializedAction, UnpinNotebookMaterializedAction, DeleteMaterializedNotebookAction } from 'sql/platform/jobManagement/browser/jobActions';
|
||||
import { NotebookCacheObject } from 'sql/platform/jobManagement/common/jobManagementService';
|
||||
import { JobManagementUtilities } from 'sql/platform/jobManagement/browser/jobManagementUtilities';
|
||||
import { IJobManagementService } from 'sql/platform/jobManagement/common/interfaces';
|
||||
import { JobHistoryRow } from 'sql/workbench/parts/jobManagement/browser/jobHistoryTree';
|
||||
import { JobStepsViewRow } from 'sql/workbench/parts/jobManagement/browser/jobStepsViewTree';
|
||||
import { IWorkbenchThemeService } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { JobManagementView, JobActionContext } from 'sql/workbench/parts/jobManagement/browser/jobManagementView';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
|
||||
|
||||
export const DASHBOARD_SELECTOR: string = 'notebookhistory-component';
|
||||
export class GridSection {
|
||||
title: string;
|
||||
histories: azdata.AgentNotebookHistoryInfo[];
|
||||
contextMenuType: number;
|
||||
style: string;
|
||||
}
|
||||
@Component({
|
||||
selector: DASHBOARD_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./notebookHistory.component.html')),
|
||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => NotebookHistoryComponent) }],
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
@Injectable()
|
||||
export class NotebookHistoryComponent extends JobManagementView implements OnInit {
|
||||
|
||||
|
||||
|
||||
@ViewChild('table') private _tableContainer: ElementRef;
|
||||
@ViewChild('jobsteps') private _jobStepsView: ElementRef;
|
||||
@ViewChild('notebookHistoryActionbarContainer') private _notebookHistoryActionbarView: ElementRef;
|
||||
@ViewChild('notebookgriditem') private _notebookGridItem: ElementRef;
|
||||
|
||||
@Input() public agentNotebookInfo: azdata.AgentNotebookInfo = undefined;
|
||||
@Input() public agentJobHistories: azdata.AgentJobHistoryInfo[] = undefined;
|
||||
public notebookHistories: azdata.AgentNotebookHistoryInfo[] = undefined;
|
||||
public agentNotebookHistoryInfo: azdata.AgentNotebookHistoryInfo = undefined;
|
||||
|
||||
private _isVisible: boolean = false;
|
||||
private _stepRows: JobStepsViewRow[] = [];
|
||||
private _showSteps: boolean = undefined;
|
||||
private _showPreviousRuns: boolean = undefined;
|
||||
private _runStatus: string = undefined;
|
||||
private _notebookCacheObject: NotebookCacheObject;
|
||||
private _agentNotebookInfo: azdata.AgentNotebookInfo;
|
||||
private _noJobsAvailable: boolean = false;
|
||||
protected _notebookHistoryActionBar: Taskbar;
|
||||
|
||||
// Job Actions
|
||||
private _editNotebookJobAction: EditNotebookJobAction;
|
||||
private _runJobAction: RunJobAction;
|
||||
private _stopJobAction: StopJobAction;
|
||||
private _refreshAction: JobsRefreshAction;
|
||||
private _openNotebookTemplateAction: OpenTemplateNotebookAction;
|
||||
|
||||
private static readonly HEADING_HEIGHT: number = 24;
|
||||
|
||||
private _grids: GridSection[] = [];
|
||||
|
||||
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => AgentViewComponent)) _agentViewComponent: AgentViewComponent,
|
||||
@Inject(IWorkbenchThemeService) private themeService: IWorkbenchThemeService,
|
||||
@Inject(IInstantiationService) private instantiationService: IInstantiationService,
|
||||
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
|
||||
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
||||
@Inject(ICommandService) private _commandService: ICommandService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IDashboardService) dashboardService: IDashboardService,
|
||||
@Inject(ITelemetryService) private _telemetryService: ITelemetryService,
|
||||
@Inject(IQuickInputService) private _quickInputService: IQuickInputService
|
||||
) {
|
||||
super(commonService, dashboardService, contextMenuService, keybindingService, instantiationService, _agentViewComponent);
|
||||
let notebookCacheObjectMap = this._jobManagementService.notebookCacheObjectMap;
|
||||
this._serverName = commonService.connectionManagementService.connectionInfo.connectionProfile.serverName;
|
||||
let notebookCache = notebookCacheObjectMap[this._serverName];
|
||||
if (notebookCache) {
|
||||
this._notebookCacheObject = notebookCache;
|
||||
} else {
|
||||
this._notebookCacheObject = new NotebookCacheObject();
|
||||
this._notebookCacheObject.serverName = this._serverName;
|
||||
this._jobManagementService.addToCache(this._serverName, this._notebookCacheObject);
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// set base class elements
|
||||
this._visibilityElement = this._tableContainer;
|
||||
this._parentComponent = this._agentViewComponent;
|
||||
this._agentNotebookInfo = this._agentViewComponent.agentNotebookInfo;
|
||||
this.initActionBar();
|
||||
const self = this;
|
||||
this._telemetryService.publicLog(TelemetryKeys.JobHistoryView);
|
||||
}
|
||||
|
||||
private loadHistory() {
|
||||
const self = this;
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let jobName = this._agentViewComponent.agentNotebookInfo.name;
|
||||
let jobId = this._agentViewComponent.notebookId;
|
||||
let targetDatabase = this._agentViewComponent.agentNotebookInfo.targetDatabase;
|
||||
this._jobManagementService.getNotebookHistory(ownerUri, jobId, jobName, targetDatabase).then((result) => {
|
||||
if (result && result.histories) {
|
||||
this.notebookHistories = result.histories;
|
||||
self._notebookCacheObject.setNotebookHistory(jobId, this.notebookHistories);
|
||||
self._notebookCacheObject.setJobSchedules(jobId, result.schedules);
|
||||
self._notebookCacheObject.setJobSteps(jobId, result.steps);
|
||||
this._agentViewComponent.agentNotebookInfo.jobSteps = this._notebookCacheObject.getJobSteps(jobId);
|
||||
this._agentViewComponent.agentNotebookInfo.jobSchedules = this._notebookCacheObject.getJobSchedules(jobId);
|
||||
this._agentNotebookInfo = this._agentViewComponent.agentNotebookInfo;
|
||||
if (result.histories.length > 0) {
|
||||
self._noJobsAvailable = false;
|
||||
self._showPreviousRuns = true;
|
||||
} else {
|
||||
self._notebookCacheObject.setNotebookHistory(self._agentViewComponent.notebookId, result.histories);
|
||||
self._noJobsAvailable = true;
|
||||
self._showPreviousRuns = false;
|
||||
}
|
||||
} else {
|
||||
self._noJobsAvailable = true;
|
||||
self._showPreviousRuns = false;
|
||||
self._showSteps = false;
|
||||
}
|
||||
this._actionBar.context = { targetObject: { canEdit: true, notebook: this._agentNotebookInfo, job: this._agentNotebookInfo }, ownerUri: this.ownerUri, component: this };
|
||||
this._editNotebookJobAction.enabled = true;
|
||||
this._actionBar.setContent([
|
||||
{ action: this._runJobAction },
|
||||
{ action: this._stopJobAction },
|
||||
{ action: this._refreshAction },
|
||||
{ action: this._editNotebookJobAction },
|
||||
{ action: this._openNotebookTemplateAction }
|
||||
]);
|
||||
|
||||
this.createGrid();
|
||||
if (self._agentViewComponent.showNotebookHistory) {
|
||||
self._cd.detectChanges();
|
||||
this.collapseGrid();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private setStepsTree(element: JobHistoryRow) {
|
||||
const self = this;
|
||||
let cachedHistory = self._notebookCacheObject.getNotebookHistory(element.jobID);
|
||||
if (cachedHistory) {
|
||||
self.agentNotebookHistoryInfo = cachedHistory.find(
|
||||
history => self.formatTime(history.runDate) === self.formatTime(element.runDate));
|
||||
}
|
||||
if (self.agentNotebookHistoryInfo) {
|
||||
self.agentNotebookHistoryInfo.runDate = self.formatTime(self.agentNotebookHistoryInfo.runDate);
|
||||
if (self.agentNotebookHistoryInfo.steps) {
|
||||
let jobStepStatus = this.didJobFail(self.agentNotebookHistoryInfo);
|
||||
self._stepRows = self.agentNotebookHistoryInfo.steps.map(step => {
|
||||
let stepViewRow = new JobStepsViewRow();
|
||||
stepViewRow.message = step.message;
|
||||
stepViewRow.runStatus = jobStepStatus ? JobManagementUtilities.convertToStatusString(0) :
|
||||
JobManagementUtilities.convertToStatusString(step.runStatus);
|
||||
self._runStatus = JobManagementUtilities.convertToStatusString(self.agentNotebookHistoryInfo.runStatus);
|
||||
stepViewRow.stepName = step.stepDetails.stepName;
|
||||
stepViewRow.stepId = step.stepDetails.id.toString();
|
||||
return stepViewRow;
|
||||
});
|
||||
self._stepRows.unshift(new JobStepsViewRow());
|
||||
self._stepRows[0].rowID = 'stepsColumn' + self._agentNotebookInfo.jobId;
|
||||
self._stepRows[0].stepId = nls.localize('stepRow.stepID', "Step ID");
|
||||
self._stepRows[0].stepName = nls.localize('stepRow.stepName', "Step Name");
|
||||
self._stepRows[0].message = nls.localize('stepRow.message', "Message");
|
||||
this._showSteps = self._stepRows.length > 1;
|
||||
} else {
|
||||
self._showSteps = false;
|
||||
}
|
||||
if (self._agentViewComponent.showNotebookHistory) {
|
||||
self._cd.detectChanges();
|
||||
this.collapseGrid();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private didJobFail(job: azdata.AgentJobHistoryInfo): boolean {
|
||||
for (let i = 0; i < job.steps.length; i++) {
|
||||
if (job.steps[i].runStatus === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private toggleCollapse(): void {
|
||||
let arrow: HTMLElement = jQuery('.resultsViewCollapsible').get(0);
|
||||
let checkbox: any = document.getElementById('accordion');
|
||||
if (arrow.className === 'resultsViewCollapsible' && checkbox.checked === false) {
|
||||
arrow.className = 'resultsViewCollapsible collapsed';
|
||||
} else if (arrow.className === 'resultsViewCollapsible collapsed' && checkbox.checked === true) {
|
||||
arrow.className = 'resultsViewCollapsible';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private toggleGridCollapse(i): void {
|
||||
let notebookGrid = document.getElementById('notebook-grid' + i);
|
||||
let checkbox: any = document.getElementById('accordion' + i);
|
||||
let arrow = document.getElementById('history-grid-icon' + i);
|
||||
if (notebookGrid.className === 'notebook-grid ' + i && checkbox.checked === true) {
|
||||
notebookGrid.className = 'notebook-grid ' + i + ' collapsed';
|
||||
notebookGrid.style.display = 'none';
|
||||
arrow.className = 'resultsViewCollapsible collapsed';
|
||||
} else if (notebookGrid.className === 'notebook-grid ' + i + ' collapsed' && checkbox.checked === false) {
|
||||
notebookGrid.className = 'notebook-grid ' + i;
|
||||
notebookGrid.style.display = 'grid';
|
||||
arrow.className = 'resultsViewCollapsible';
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private toggleHistoryDisplay(event): void {
|
||||
let header = event.srcElement.attributes;
|
||||
}
|
||||
|
||||
private goToJobs(): void {
|
||||
this._isVisible = false;
|
||||
this._agentViewComponent.showNotebookHistory = false;
|
||||
}
|
||||
|
||||
private convertToJobHistoryRow(historyInfo: azdata.AgentJobHistoryInfo): JobHistoryRow {
|
||||
let jobHistoryRow = new JobHistoryRow();
|
||||
jobHistoryRow.runDate = this.formatTime(historyInfo.runDate);
|
||||
jobHistoryRow.runStatus = JobManagementUtilities.convertToStatusString(historyInfo.runStatus);
|
||||
jobHistoryRow.instanceID = historyInfo.instanceId;
|
||||
jobHistoryRow.jobID = historyInfo.jobId;
|
||||
return jobHistoryRow;
|
||||
}
|
||||
|
||||
private formatTime(time: string): string {
|
||||
|
||||
return time.replace('T', ' ');
|
||||
}
|
||||
|
||||
private formatDateTimetoLocaleDate(time: string) {
|
||||
let dateInstance = new Date(time);
|
||||
return dateInstance.toLocaleDateString();
|
||||
}
|
||||
|
||||
private formatDateTimetoLocaleTime(time: string) {
|
||||
let dateInstance = new Date(time);
|
||||
return dateInstance.toLocaleTimeString();
|
||||
}
|
||||
|
||||
|
||||
private showProgressWheel(): boolean {
|
||||
return this._showPreviousRuns !== true && this._noJobsAvailable === false;
|
||||
}
|
||||
|
||||
public onFirstVisible() {
|
||||
this._agentNotebookInfo = this._agentViewComponent.agentNotebookInfo;
|
||||
|
||||
if (!this.agentNotebookInfo) {
|
||||
this.agentNotebookInfo = this._agentNotebookInfo;
|
||||
}
|
||||
|
||||
if (this.isRefreshing) {
|
||||
this.loadHistory();
|
||||
return;
|
||||
}
|
||||
else {
|
||||
this.createGrid();
|
||||
}
|
||||
let notebookHistories = this._notebookCacheObject.notebookHistories[this._agentViewComponent.notebookId];
|
||||
if (notebookHistories) {
|
||||
if (notebookHistories.length > 0) {
|
||||
const self = this;
|
||||
this._noJobsAvailable = false;
|
||||
if (this._notebookCacheObject.prevJobID === this._agentViewComponent.notebookId || notebookHistories[0].jobId === this._agentViewComponent.notebookId) {
|
||||
this._showPreviousRuns = true;
|
||||
this._agentViewComponent.agentNotebookInfo.jobSteps = this._notebookCacheObject.getJobSteps(this._agentNotebookInfo.jobId);
|
||||
this._agentViewComponent.agentNotebookInfo.jobSchedules = this._notebookCacheObject.getJobSchedules(this._agentNotebookInfo.jobId);
|
||||
this._agentNotebookInfo = this._agentViewComponent.agentNotebookInfo;
|
||||
}
|
||||
} else if (notebookHistories.length === 0) {
|
||||
this._showPreviousRuns = false;
|
||||
this._showSteps = false;
|
||||
this._noJobsAvailable = true;
|
||||
}
|
||||
this._editNotebookJobAction.enabled = true;
|
||||
this._actionBar.setContent([
|
||||
{ action: this._runJobAction },
|
||||
{ action: this._stopJobAction },
|
||||
{ action: this._refreshAction },
|
||||
{ action: this._editNotebookJobAction },
|
||||
{ action: this._openNotebookTemplateAction }
|
||||
]);
|
||||
this._cd.detectChanges();
|
||||
this.collapseGrid();
|
||||
} else {
|
||||
this.loadHistory();
|
||||
}
|
||||
this._notebookCacheObject.prevJobID = this._agentViewComponent.notebookId;
|
||||
|
||||
}
|
||||
|
||||
public layout() {
|
||||
let historyDetails = jQuery('.overview-container').get(0);
|
||||
let statusBar = jQuery('.part.statusbar').get(0);
|
||||
if (historyDetails && statusBar) {
|
||||
let historyBottom = historyDetails.getBoundingClientRect().bottom;
|
||||
let statusTop = statusBar.getBoundingClientRect().top;
|
||||
|
||||
let height: number = statusTop - historyBottom - NotebookHistoryComponent.HEADING_HEIGHT;
|
||||
|
||||
if (this._table) {
|
||||
this._table.layout(new dom.Dimension(
|
||||
dom.getContentWidth(this._tableContainer.nativeElement),
|
||||
height));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected initActionBar() {
|
||||
this._runJobAction = this.instantiationService.createInstance(RunJobAction);
|
||||
this._stopJobAction = this.instantiationService.createInstance(StopJobAction);
|
||||
this._editNotebookJobAction = this.instantiationService.createInstance(EditNotebookJobAction);
|
||||
this._refreshAction = this.instantiationService.createInstance(JobsRefreshAction);
|
||||
this._openNotebookTemplateAction = this.instantiationService.createInstance(OpenTemplateNotebookAction);
|
||||
let taskbar = <HTMLElement>this.actionBarContainer.nativeElement;
|
||||
this._actionBar = new Taskbar(taskbar);
|
||||
this._editNotebookJobAction.enabled = !this.showProgressWheel();
|
||||
let targetObject: JobActionContext = { canEdit: !this.showProgressWheel(), job: this._agentNotebookInfo };
|
||||
this._actionBar.context = { targetObject: targetObject, ownerUri: this.ownerUri, component: this };
|
||||
this._actionBar.setContent([
|
||||
{ action: this._runJobAction },
|
||||
{ action: this._stopJobAction },
|
||||
{ action: this._refreshAction },
|
||||
{ action: this._editNotebookJobAction },
|
||||
{ action: this._openNotebookTemplateAction }
|
||||
]);
|
||||
}
|
||||
|
||||
public openNotebook(history: azdata.AgentNotebookHistoryInfo) {
|
||||
if (history.runStatus === 0) {
|
||||
return;
|
||||
}
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let targetDatabase = this._agentViewComponent.agentNotebookInfo.targetDatabase;
|
||||
this._jobManagementService.getMaterialziedNotebook(ownerUri, targetDatabase, history.materializedNotebookId).then(async (result) => {
|
||||
if (result) {
|
||||
let regex = /:|-/gi;
|
||||
let readableDataTimeString = history.runDate.replace(regex, '').replace(' ', '');
|
||||
let tempNotebookFileName = this._agentViewComponent.agentNotebookInfo.name + '_' + readableDataTimeString;
|
||||
await this._commandService.executeCommand('agent.openNotebookEditorFromJsonString', tempNotebookFileName, result.notebookMaterialized);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public deleteMaterializedNotebook(history: azdata.AgentNotebookHistoryInfo) {
|
||||
//TODO: Implement deletenotebook context menu action
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let targetDatabase = this._agentViewComponent.agentNotebookInfo.targetDatabase;
|
||||
this._jobManagementService.deleteMaterializedNotebook(ownerUri, history, targetDatabase).then(async (result) => {
|
||||
if (result) {
|
||||
this.loadHistory();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public openTemplateNotebook() {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let targetDatabase = this._agentViewComponent.agentNotebookInfo.targetDatabase;
|
||||
let jobId = this._agentViewComponent.agentNotebookInfo.jobId;
|
||||
|
||||
this._jobManagementService.getTemplateNotebook(ownerUri, targetDatabase, jobId).then(async (result) => {
|
||||
if (result) {
|
||||
await this._commandService.executeCommand('agent.openNotebookEditorFromJsonString', this._agentViewComponent.agentNotebookInfo.name, result.notebookTemplate, this.agentNotebookInfo, ownerUri);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public renameNotebook(history: azdata.AgentNotebookHistoryInfo) {
|
||||
const defaultDateTime = new Date(history.runDate).toLocaleDateString() + ' ' + new Date(history.runDate).toLocaleTimeString();
|
||||
let notebookRunName = (history.materializedNotebookName === '') ? defaultDateTime : history.materializedNotebookName;
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let targetDatabase = this._agentViewComponent.agentNotebookInfo.targetDatabase;
|
||||
let materializedNotebookId = history.materializedNotebookId;
|
||||
this._quickInputService.input({ placeHolder: notebookRunName }).then(async (value) => {
|
||||
if (value) {
|
||||
if (!/\S/.test(value)) {
|
||||
value = '';
|
||||
}
|
||||
await this._jobManagementService.updateNotebookMaterializedName(ownerUri, history, targetDatabase, value).then(async (result) => {
|
||||
if (result) {
|
||||
history.materializedNotebookName = value;
|
||||
this.loadHistory();
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public toggleNotebookPin(history: azdata.AgentNotebookHistoryInfo, pin: boolean) {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
let targetDatabase = this._agentViewComponent.agentNotebookInfo.targetDatabase;
|
||||
let materializedNotebookId = history.materializedNotebookId;
|
||||
this._jobManagementService.updateNotebookMaterializedPin(ownerUri, history, targetDatabase, pin).then(async (result) => {
|
||||
if (result) {
|
||||
history.materializedNotebookPin = pin;
|
||||
this.loadHistory();
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
public openHistoryContextMenu(event: MouseEvent, history: azdata.AgentNotebookHistoryInfo, contextMenuType: number) {
|
||||
let anchor = {
|
||||
x: event.clientX,
|
||||
y: event.clientY
|
||||
};
|
||||
let runDate = event.target['runDate'];
|
||||
let gridActions = this.getGridActions();
|
||||
let actionContext = {
|
||||
component: this,
|
||||
history: history
|
||||
};
|
||||
this._contextMenuService.showContextMenu({
|
||||
getAnchor: () => anchor,
|
||||
getActions: () => (contextMenuType === 1) ? this.getPinnedGridActions() : this.getGridActions(),
|
||||
getKeyBinding: (action) => this._keybindingFor(action),
|
||||
getActionsContext: () => (actionContext)
|
||||
});
|
||||
}
|
||||
|
||||
protected getGridActions(): IAction[] {
|
||||
const openNotebookAction = this._instantiationService.createInstance(OpenMaterializedNotebookAction);
|
||||
const renameNotebookAction = this._instantiationService.createInstance(RenameNotebookMaterializedAction);
|
||||
const pinNotebookAction = this._instantiationService.createInstance(PinNotebookMaterializedAction);
|
||||
const deleteMaterializedNotebookAction = this._instantiationService.createInstance(DeleteMaterializedNotebookAction);
|
||||
return [
|
||||
openNotebookAction,
|
||||
renameNotebookAction,
|
||||
pinNotebookAction,
|
||||
deleteMaterializedNotebookAction
|
||||
];
|
||||
}
|
||||
|
||||
protected getPinnedGridActions(): IAction[] {
|
||||
const openNotebookAction = this._instantiationService.createInstance(OpenMaterializedNotebookAction);
|
||||
const renameNotebookAction = this._instantiationService.createInstance(RenameNotebookMaterializedAction);
|
||||
const unpinNotebookAction = this._instantiationService.createInstance(UnpinNotebookMaterializedAction);
|
||||
const deleteMaterializedNotebookAction = this._instantiationService.createInstance(DeleteMaterializedNotebookAction);
|
||||
return [
|
||||
openNotebookAction,
|
||||
renameNotebookAction,
|
||||
unpinNotebookAction,
|
||||
deleteMaterializedNotebookAction
|
||||
];
|
||||
}
|
||||
|
||||
public createdTooltip(history: azdata.AgentNotebookHistoryInfo) {
|
||||
let tooltipString: string = '';
|
||||
if (history.materializedNotebookName && history.materializedNotebookName !== '') {
|
||||
tooltipString = history.materializedNotebookName;
|
||||
}
|
||||
let dateOptions = {
|
||||
weekday: 'long',
|
||||
year: 'numeric',
|
||||
month: 'long',
|
||||
day: 'numeric'
|
||||
};
|
||||
|
||||
tooltipString += '\n' + nls.localize('notebookHistory.dateCreatedTooltip', "Date Created: ") + new Date(history.runDate).toLocaleDateString(undefined, dateOptions);
|
||||
if (history.materializedNotebookErrorInfo && /\S/.test(history.materializedNotebookErrorInfo)) {
|
||||
tooltipString += '\n' + nls.localize('notebookHistory.notebookErrorTooltip', "Notebook Error: ") + history.materializedNotebookErrorInfo;
|
||||
}
|
||||
if (history.runStatus === 0 && history.message && /\S/.test(history.message)) {
|
||||
tooltipString += '\n' + nls.localize('notebookHistory.ErrorTooltip', "Job Error: ") + history.message;
|
||||
}
|
||||
return tooltipString;
|
||||
}
|
||||
|
||||
public createGrid() {
|
||||
let histories = this._notebookCacheObject.getNotebookHistory(this._agentViewComponent.notebookId);
|
||||
histories = histories.sort((h1, h2) => {
|
||||
return new Date(h2.runDate).getTime() - new Date(h1.runDate).getTime();
|
||||
});
|
||||
this._grids = [];
|
||||
let tempHistory: azdata.AgentNotebookHistoryInfo[] = [];
|
||||
for (let i = 0; i < histories.length; i++) {
|
||||
if (histories[i].materializedNotebookPin) {
|
||||
tempHistory.push(histories[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this._grids.push({
|
||||
title: nls.localize('notebookHistory.pinnedTitle', "Pinned"),
|
||||
histories: tempHistory,
|
||||
contextMenuType: 1,
|
||||
style: 'grid'
|
||||
});
|
||||
// Pushing the pinned notebooks grid
|
||||
tempHistory = [];
|
||||
let count = 0;
|
||||
let i = 0;
|
||||
for (; i < histories.length; i++) {
|
||||
if (!histories[i].materializedNotebookPin && count < 10) {
|
||||
tempHistory.push(histories[i]);
|
||||
count++;
|
||||
}
|
||||
if (count === 10) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this._grids.push({
|
||||
title: nls.localize('notebookHistory.recentRunsTitle', "Recent Runs"),
|
||||
histories: tempHistory,
|
||||
contextMenuType: 0,
|
||||
style: 'grid'
|
||||
});
|
||||
tempHistory = [];
|
||||
for (i += 1; i < histories.length; i++) {
|
||||
if (!histories[i].materializedNotebookPin) {
|
||||
tempHistory.push(histories[i]);
|
||||
}
|
||||
}
|
||||
this._grids.push({
|
||||
title: nls.localize('notebookHistory.pastRunsTitle', "Past Runs"),
|
||||
histories: tempHistory,
|
||||
contextMenuType: 0,
|
||||
style: 'none'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
public collapseGrid() {
|
||||
for (let i = 0; i < this._grids.length; i++) {
|
||||
let notebookGrid = document.getElementById('notebook-grid' + i);
|
||||
let arrow = document.getElementById('history-grid-icon' + i);
|
||||
if (notebookGrid) {
|
||||
let checkbox: any = document.getElementById('accordion' + i);
|
||||
if (this._grids[i].style === 'none') {
|
||||
notebookGrid.className = 'notebook-grid ' + i + ' collapsed';
|
||||
arrow.className = 'resultsViewCollapsible collapsed';
|
||||
notebookGrid.style.display = 'none';
|
||||
checkbox.checked = true;
|
||||
}
|
||||
else {
|
||||
notebookGrid.className = 'notebook-grid ' + i;
|
||||
notebookGrid.style.display = 'grid';
|
||||
arrow.className = 'resultsViewCollapsible';
|
||||
checkbox.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public refreshJobs() {
|
||||
this._agentViewComponent.refresh = true;
|
||||
this.loadHistory();
|
||||
}
|
||||
|
||||
/** GETTERS */
|
||||
|
||||
public get showSteps(): boolean {
|
||||
return this._showSteps;
|
||||
}
|
||||
|
||||
public get stepRows() {
|
||||
return this._stepRows;
|
||||
}
|
||||
|
||||
public get ownerUri(): string {
|
||||
return this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
}
|
||||
|
||||
public get serverName(): string {
|
||||
return this._serverName;
|
||||
}
|
||||
|
||||
/** SETTERS */
|
||||
|
||||
public set showSteps(value: boolean) {
|
||||
this._showSteps = value;
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<!--
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
-->
|
||||
<div class="job-heading-container">
|
||||
<h1 class="job-heading" *ngIf="_isCloud === false">Notebook Jobs</h1>
|
||||
<h1 class="job-heading" *ngIf="_isCloud === true">No Notebooks Jobs Available</h1>
|
||||
<div class="icon in-progress" *ngIf="_showProgressWheel === true"></div>
|
||||
</div>
|
||||
|
||||
<div #actionbarContainer class="agent-actionbar-container"></div>
|
||||
|
||||
<div #notebooksgrid class="jobnotebooksview-grid"></div>
|
||||
@@ -0,0 +1,984 @@
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the Source EULA. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import 'vs/css!./media/jobs';
|
||||
|
||||
import * as azdata from 'azdata';
|
||||
import * as nls from 'vs/nls';
|
||||
import * as dom from 'vs/base/browser/dom';
|
||||
import { Component, Inject, forwardRef, ElementRef, ChangeDetectorRef, ViewChild, OnInit, OnDestroy } from '@angular/core';
|
||||
import { TabChild } from 'sql/base/browser/ui/panel/tab.component';
|
||||
import { Table } from 'sql/base/browser/ui/table/table';
|
||||
import { AgentViewComponent } from 'sql/workbench/parts/jobManagement/browser/agentView.component';
|
||||
import { RowDetailView } from 'sql/base/browser/ui/table/plugins/rowDetailView';
|
||||
import { NotebookCacheObject } from 'sql/platform/jobManagement/common/jobManagementService';
|
||||
import { EditJobAction, NewNotebookJobAction, RunJobAction, EditNotebookJobAction, JobsRefreshAction, IJobActionInfo, DeleteNotebookAction } from 'sql/platform/jobManagement/browser/jobActions';
|
||||
import { JobManagementUtilities } from 'sql/platform/jobManagement/browser/jobManagementUtilities';
|
||||
import { HeaderFilter } from 'sql/base/browser/ui/table/plugins/headerFilter.plugin';
|
||||
import { IJobManagementService } from 'sql/platform/jobManagement/common/interfaces';
|
||||
import { JobManagementView, JobActionContext } from 'sql/workbench/parts/jobManagement/browser/jobManagementView';
|
||||
import { CommonServiceInterface } from 'sql/platform/bootstrap/browser/commonServiceInterface.service';
|
||||
import { ICommandService } from 'vs/platform/commands/common/commands';
|
||||
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
|
||||
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
|
||||
import { IDashboardService } from 'sql/platform/dashboard/browser/dashboardService';
|
||||
import { escape } from 'sql/base/common/strings';
|
||||
import { IWorkbenchThemeService, IColorTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { tableBackground, cellBackground, cellBorderColor } from 'sql/platform/theme/common/colors';
|
||||
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
|
||||
import * as TelemetryKeys from 'sql/platform/telemetry/common/telemetryKeys';
|
||||
import { attachButtonStyler } from 'sql/platform/theme/common/styler';
|
||||
import { Taskbar } from 'sql/base/browser/ui/taskbar/taskbar';
|
||||
|
||||
|
||||
export const NOTEBOOKSVIEW_SELECTOR: string = 'notebooksview-component';
|
||||
export const ROW_HEIGHT: number = 45;
|
||||
export const ACTIONBAR_PADDING: number = 10;
|
||||
|
||||
interface IItem extends Slick.SlickData {
|
||||
notebookId?: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: NOTEBOOKSVIEW_SELECTOR,
|
||||
templateUrl: decodeURI(require.toUrl('./notebooksView.component.html')),
|
||||
providers: [{ provide: TabChild, useExisting: forwardRef(() => NotebooksViewComponent) }],
|
||||
})
|
||||
|
||||
export class NotebooksViewComponent extends JobManagementView implements OnInit, OnDestroy {
|
||||
|
||||
private columns: Array<Slick.Column<any>> = [
|
||||
{
|
||||
name: nls.localize('notebookColumns.name', "Name"),
|
||||
field: 'name',
|
||||
formatter: (row, cell, value, columnDef, dataContext) => this.renderName(row, cell, value, columnDef, dataContext),
|
||||
width: 150,
|
||||
id: 'name'
|
||||
},
|
||||
{ name: nls.localize('notebookColumns.targetDatbase', "Target Database"), field: 'targetDatabase', width: 80, id: 'targetDatabase' },
|
||||
{ name: nls.localize('notebookColumns.lastRun', "Last Run"), field: 'lastRun', width: 80, id: 'lastRun' },
|
||||
{ name: nls.localize('notebookColumns.nextRun', "Next Run"), field: 'nextRun', width: 80, id: 'nextRun' },
|
||||
{ name: nls.localize('notebookColumns.status', "Status"), field: 'currentExecutionStatus', width: 50, id: 'currentExecutionStatus' },
|
||||
{ name: nls.localize('notebookColumns.lastRunOutcome', "Last Run Outcome"), field: 'lastRunOutcome', width: 100, id: 'lastRunOutcome' },
|
||||
{
|
||||
name: nls.localize('notebookColumns.previousRuns', "Previous Runs"),
|
||||
formatter: (row, cell, value, columnDef, dataContext) => this.renderChartsPostHistory(row, cell, value, columnDef, dataContext),
|
||||
field: 'previousRuns',
|
||||
width: 100,
|
||||
id: 'previousRuns'
|
||||
}
|
||||
];
|
||||
|
||||
private _notebookCacheObject: NotebookCacheObject;
|
||||
private rowDetail: RowDetailView<IItem>;
|
||||
private filterPlugin: any;
|
||||
private dataView: any;
|
||||
private _isCloud: boolean;
|
||||
private filterStylingMap: { [columnName: string]: [any]; } = {};
|
||||
private filterStack = ['start'];
|
||||
private filterValueMap: { [columnName: string]: string[]; } = {};
|
||||
private sortingStylingMap: { [columnName: string]: any; } = {};
|
||||
|
||||
public notebooks: azdata.AgentNotebookInfo[];
|
||||
private notebookHistories: { [jobId: string]: azdata.AgentNotebookHistoryInfo[]; } = Object.create(null);
|
||||
private jobSteps: { [jobId: string]: azdata.AgentJobStepInfo[]; } = Object.create(null);
|
||||
private jobAlerts: { [jobId: string]: azdata.AgentAlertInfo[]; } = Object.create(null);
|
||||
private jobSchedules: { [jobId: string]: azdata.AgentJobScheduleInfo[]; } = Object.create(null);
|
||||
public contextAction = NewNotebookJobAction;
|
||||
|
||||
@ViewChild('notebooksgrid') _gridEl: ElementRef;
|
||||
|
||||
constructor(
|
||||
@Inject(forwardRef(() => CommonServiceInterface)) commonService: CommonServiceInterface,
|
||||
@Inject(forwardRef(() => ChangeDetectorRef)) private _cd: ChangeDetectorRef,
|
||||
@Inject(forwardRef(() => ElementRef)) private _el: ElementRef,
|
||||
@Inject(forwardRef(() => AgentViewComponent)) _agentViewComponent: AgentViewComponent,
|
||||
@Inject(IJobManagementService) private _jobManagementService: IJobManagementService,
|
||||
@Inject(IWorkbenchThemeService) private _themeService: IWorkbenchThemeService,
|
||||
@Inject(ICommandService) private _commandService: ICommandService,
|
||||
@Inject(IInstantiationService) instantiationService: IInstantiationService,
|
||||
@Inject(IContextMenuService) contextMenuService: IContextMenuService,
|
||||
@Inject(IKeybindingService) keybindingService: IKeybindingService,
|
||||
@Inject(IDashboardService) _dashboardService: IDashboardService,
|
||||
@Inject(ITelemetryService) private _telemetryService: ITelemetryService
|
||||
) {
|
||||
super(commonService, _dashboardService, contextMenuService, keybindingService, instantiationService, _agentViewComponent);
|
||||
let notebookCacheObjectMap = this._jobManagementService.notebookCacheObjectMap;
|
||||
let jobCache = notebookCacheObjectMap[this._serverName];
|
||||
if (jobCache) {
|
||||
this._notebookCacheObject = jobCache;
|
||||
} else {
|
||||
this._notebookCacheObject = new NotebookCacheObject();
|
||||
this._notebookCacheObject.serverName = this._serverName;
|
||||
this._jobManagementService.addToCache(this._serverName, this._notebookCacheObject);
|
||||
}
|
||||
this._isCloud = commonService.connectionManagementService.connectionInfo.serverInfo.isCloud;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
// set base class elements
|
||||
this._visibilityElement = this._gridEl;
|
||||
this._parentComponent = this._agentViewComponent;
|
||||
this._register(this._themeService.onDidColorThemeChange(e => this.updateTheme(e)));
|
||||
this._telemetryService.publicLog(TelemetryKeys.JobsView);
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
}
|
||||
|
||||
public layout() {
|
||||
let jobsViewToolbar = jQuery('notebooksview-component .agent-actionbar-container').get(0);
|
||||
let statusBar = jQuery('.part.statusbar').get(0);
|
||||
if (jobsViewToolbar && statusBar) {
|
||||
let toolbarBottom = jobsViewToolbar.getBoundingClientRect().bottom + ACTIONBAR_PADDING;
|
||||
let statusTop = statusBar.getBoundingClientRect().top;
|
||||
this._table.layout(new dom.Dimension(
|
||||
dom.getContentWidth(this._gridEl.nativeElement),
|
||||
statusTop - toolbarBottom));
|
||||
}
|
||||
}
|
||||
|
||||
onFirstVisible() {
|
||||
let self = this;
|
||||
let cached: boolean = false;
|
||||
if (this._notebookCacheObject.serverName === this._serverName && this._notebookCacheObject.notebooks.length > 0) {
|
||||
cached = true;
|
||||
this.notebooks = this._notebookCacheObject.notebooks;
|
||||
}
|
||||
|
||||
let columns = this.columns.map((column) => {
|
||||
column.rerenderOnResize = true;
|
||||
return column;
|
||||
});
|
||||
let options = <Slick.GridOptions<any>>{
|
||||
syncColumnCellResize: true,
|
||||
enableColumnReorder: false,
|
||||
rowHeight: ROW_HEIGHT,
|
||||
enableCellNavigation: true,
|
||||
forceFitColumns: false
|
||||
};
|
||||
|
||||
this.dataView = new Slick.Data.DataView({ inlineFilters: false });
|
||||
|
||||
let rowDetail = new RowDetailView<IItem>({
|
||||
cssClass: '_detail_selector',
|
||||
process: (job) => {
|
||||
(<any>rowDetail).onAsyncResponse.notify({
|
||||
'itemDetail': job
|
||||
}, undefined, this);
|
||||
},
|
||||
useRowClick: false,
|
||||
panelRows: 1,
|
||||
postTemplate: () => '', // I'm assuming these code paths are just never hit...
|
||||
preTemplate: () => '',
|
||||
});
|
||||
this.rowDetail = rowDetail;
|
||||
columns.unshift(this.rowDetail.getColumnDefinition());
|
||||
let filterPlugin = new HeaderFilter<{ inlineFilters: false }>();
|
||||
this._register(attachButtonStyler(filterPlugin, this._themeService));
|
||||
this.filterPlugin = filterPlugin;
|
||||
jQuery(this._gridEl.nativeElement).empty();
|
||||
jQuery(this.actionBarContainer.nativeElement).empty();
|
||||
this.initActionBar();
|
||||
this._table = this._register(new Table(this._gridEl.nativeElement, { columns }, options));
|
||||
this._table.grid.setData(this.dataView, true);
|
||||
this._table.grid.onClick.subscribe((e, args) => {
|
||||
let notebook = self.getNotebook(args);
|
||||
self._agentViewComponent.notebookId = notebook.jobId;
|
||||
self._agentViewComponent.agentNotebookInfo = notebook;
|
||||
self._agentViewComponent.showNotebookHistory = true;
|
||||
});
|
||||
this._register(this._table.onContextMenu(e => {
|
||||
self.openContextMenu(e);
|
||||
}));
|
||||
|
||||
if (cached && this._agentViewComponent.refresh !== true) {
|
||||
this.onNotebooksAvailable(null);
|
||||
this._showProgressWheel = false;
|
||||
if (this.isVisible) {
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
} else {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
this._jobManagementService.getNotebooks(ownerUri).then((result) => {
|
||||
if (result && result.notebooks) {
|
||||
self.notebooks = result.notebooks;
|
||||
self._notebookCacheObject.notebooks = self.notebooks;
|
||||
self.onNotebooksAvailable(result.notebooks);
|
||||
}
|
||||
this._showProgressWheel = false;
|
||||
if (this.isVisible) {
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected initActionBar() {
|
||||
let refreshAction = this._instantiationService.createInstance(JobsRefreshAction);
|
||||
let newAction = this._instantiationService.createInstance(NewNotebookJobAction);
|
||||
let taskbar = <HTMLElement>this.actionBarContainer.nativeElement;
|
||||
this._actionBar = new Taskbar(taskbar);
|
||||
this._actionBar.setContent([
|
||||
{ action: refreshAction },
|
||||
{ action: newAction }
|
||||
]);
|
||||
let context: IJobActionInfo = { component: this, ownerUri: this._commonService.connectionManagementService.connectionInfo.ownerUri };
|
||||
this._actionBar.context = context;
|
||||
}
|
||||
|
||||
private onNotebooksAvailable(notebooks: azdata.AgentNotebookInfo[]) {
|
||||
let jobViews: any;
|
||||
let start: boolean = true;
|
||||
if (!notebooks) {
|
||||
let dataView = this._notebookCacheObject.dataView;
|
||||
jobViews = dataView.getItems();
|
||||
start = false;
|
||||
} else {
|
||||
jobViews = notebooks.map((job) => {
|
||||
return {
|
||||
id: 'notebook' + job.jobId,
|
||||
notebookId: job.jobId,
|
||||
name: job.name,
|
||||
targetDatabase: job.targetDatabase,
|
||||
lastRun: JobManagementUtilities.convertToLastRun(job.lastRun),
|
||||
nextRun: JobManagementUtilities.convertToNextRun(job.nextRun),
|
||||
currentExecutionStatus: JobManagementUtilities.convertToExecutionStatusString(job.currentExecutionStatus),
|
||||
lastRunOutcome: (job.lastRunNotebookError === '') ? JobManagementUtilities.convertToStatusString(job.lastRunOutcome) : 'Notebook Error'
|
||||
};
|
||||
});
|
||||
}
|
||||
this._table.registerPlugin(<any>this.rowDetail);
|
||||
this.filterPlugin.onFilterApplied.subscribe((e, args) => {
|
||||
this.dataView.refresh();
|
||||
this._table.grid.resetActiveCell();
|
||||
let filterValues = args.column.filterValues;
|
||||
if (filterValues) {
|
||||
if (filterValues.length === 0) {
|
||||
// if an associated styling exists with the current filters
|
||||
if (this.filterStylingMap[args.column.name]) {
|
||||
let filterLength = this.filterStylingMap[args.column.name].length;
|
||||
// then remove the filtered styling
|
||||
for (let i = 0; i < filterLength; i++) {
|
||||
let lastAppliedStyle = this.filterStylingMap[args.column.name].pop();
|
||||
this._table.grid.removeCellCssStyles(lastAppliedStyle[0]);
|
||||
}
|
||||
delete this.filterStylingMap[args.column.name];
|
||||
let index = this.filterStack.indexOf(args.column.name, 0);
|
||||
if (index > -1) {
|
||||
this.filterStack.splice(index, 1);
|
||||
delete this.filterValueMap[args.column.name];
|
||||
}
|
||||
// apply the previous filter styling
|
||||
let currentItems = this.dataView.getFilteredItems();
|
||||
let styledItems = this.filterValueMap[this.filterStack[this.filterStack.length - 1]][1];
|
||||
if (styledItems === currentItems) {
|
||||
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]);
|
||||
}
|
||||
} else {
|
||||
// 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('notebook-error-row' + i.toString());
|
||||
let item = this.dataView.getFilteredItems()[i];
|
||||
if (item.lastRunOutcome === 'Failed') {
|
||||
this.addToStyleHash(seenJobs, false, this.filterStylingMap, args.column.name);
|
||||
if (this.filterStack.indexOf(args.column.name) < 0) {
|
||||
this.filterStack.push(args.column.name);
|
||||
this.filterValueMap[args.column.name] = [filterValues];
|
||||
}
|
||||
// one expansion for the row and one for
|
||||
// the error detail
|
||||
seenJobs++;
|
||||
i++;
|
||||
}
|
||||
seenJobs++;
|
||||
}
|
||||
this.dataView.refresh();
|
||||
this.filterValueMap[args.column.name].push(this.dataView.getFilteredItems());
|
||||
this._table.grid.resetActiveCell();
|
||||
}
|
||||
if (this.filterStack.length === 0) {
|
||||
this.filterStack = ['start'];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let seenNotebooks = 0;
|
||||
for (let i = 0; i < this.notebooks.length; i++) {
|
||||
this._table.grid.removeCellCssStyles('error-row' + i.toString());
|
||||
this._table.grid.removeCellCssStyles('notebook-error-row' + i.toString());
|
||||
let item = this.dataView.getItemByIdx(i);
|
||||
// current filter
|
||||
if (_.contains(filterValues, item[args.column.field])) {
|
||||
// check all previous filters
|
||||
if (this.checkPreviousFilters(item)) {
|
||||
if (item.lastRunOutcome === 'Failed') {
|
||||
this.addToStyleHash(seenNotebooks, false, this.filterStylingMap, args.column.name);
|
||||
if (this.filterStack.indexOf(args.column.name) < 0) {
|
||||
this.filterStack.push(args.column.name);
|
||||
this.filterValueMap[args.column.name] = [filterValues];
|
||||
}
|
||||
// one expansion for the row and one for
|
||||
// the error detail
|
||||
seenNotebooks++;
|
||||
i++;
|
||||
}
|
||||
seenNotebooks++;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.dataView.refresh();
|
||||
if (this.filterValueMap[args.column.name]) {
|
||||
this.filterValueMap[args.column.name].push(this.dataView.getFilteredItems());
|
||||
} else {
|
||||
this.filterValueMap[args.column.name] = this.dataView.getFilteredItems();
|
||||
}
|
||||
|
||||
this._table.grid.resetActiveCell();
|
||||
}
|
||||
} else {
|
||||
this.expandJobs(false);
|
||||
}
|
||||
});
|
||||
|
||||
this.filterPlugin.onCommand.subscribe((e, args: any) => {
|
||||
this.columnSort(args.column.name, args.command === 'sort-asc');
|
||||
});
|
||||
this._table.registerPlugin(this.filterPlugin);
|
||||
|
||||
this.dataView.beginUpdate();
|
||||
this.dataView.setItems(jobViews);
|
||||
this.dataView.setFilter((item) => this.filter(item));
|
||||
this.dataView.endUpdate();
|
||||
this._table.autosizeColumns();
|
||||
this._table.resizeCanvas();
|
||||
|
||||
this.expandJobs(start);
|
||||
// tooltip for job name
|
||||
jQuery('.jobview-jobnamerow').hover(e => {
|
||||
let currentTarget = e.currentTarget;
|
||||
currentTarget.title = currentTarget.innerText;
|
||||
});
|
||||
|
||||
const self = this;
|
||||
this._table.grid.onColumnsResized.subscribe((e, data: any) => {
|
||||
let nameWidth: number = data.grid.getColumns()[1].width;
|
||||
// adjust job name when resized
|
||||
jQuery('#notebooksDiv .jobnotebooksview-grid .slick-cell.l1.r1 .jobview-jobnametext').css('width', `${nameWidth - 10}px`);
|
||||
// adjust error message when resized
|
||||
jQuery('#notebooksDiv .jobnotebooksview-grid .slick-cell.l1.r1.error-row .jobview-jobnametext').css('width', '100%');
|
||||
jQuery('#notebooksDiv .jobnotebooksview-grid .slick-cell.l1.r1.notebook-error-row .jobview-jobnametext').css('width', '100%');
|
||||
|
||||
// generate job charts again
|
||||
self.notebooks.forEach(job => {
|
||||
let jobHistories = self._notebookCacheObject.getNotebookHistory(job.jobId);
|
||||
if (jobHistories) {
|
||||
let previousRuns = jobHistories.slice(jobHistories.length - 5, jobHistories.length);
|
||||
self.createJobChart('notebook' + job.jobId, previousRuns);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
jQuery('#notebooksDiv .jobnotebooksview-grid .monaco-table .slick-viewport .grid-canvas .ui-widget-content.slick-row').hover((e1) =>
|
||||
this.highlightErrorRows(e1), (e2) => this.hightlightNonErrorRows(e2));
|
||||
|
||||
this._table.grid.onScroll.subscribe((e) => {
|
||||
jQuery('#notebooksDiv .jobnotebooksview-grid .monaco-table .slick-viewport .grid-canvas .ui-widget-content.slick-row').hover((e1) =>
|
||||
this.highlightErrorRows(e1), (e2) => this.hightlightNonErrorRows(e2));
|
||||
});
|
||||
|
||||
// cache the dataview for future use
|
||||
this._notebookCacheObject.dataView = this.dataView;
|
||||
this.filterValueMap['start'] = [[], this.dataView.getItems()];
|
||||
this.loadJobHistories();
|
||||
}
|
||||
|
||||
private highlightErrorRows(e) {
|
||||
// highlight the error row as well if a failing job row is hovered
|
||||
if (e.currentTarget.children.item(0).classList.contains('job-with-error')) {
|
||||
let target = jQuery(e.currentTarget);
|
||||
let targetChildren = jQuery(e.currentTarget.children);
|
||||
let siblings = target.nextAll().toArray();
|
||||
let top = parseInt(target.css('top'), 10);
|
||||
for (let i = 0; i < siblings.length; i++) {
|
||||
let sibling = siblings[i];
|
||||
let siblingTop = parseInt(jQuery(sibling).css('top'), 10);
|
||||
if (siblingTop === top + ROW_HEIGHT) {
|
||||
jQuery(sibling.children).addClass('hovered');
|
||||
sibling.onmouseenter = (e) => {
|
||||
targetChildren.addClass('hovered');
|
||||
};
|
||||
sibling.onmouseleave = (e) => {
|
||||
targetChildren.removeClass('hovered');
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private hightlightNonErrorRows(e) {
|
||||
// switch back to original background
|
||||
if (e.currentTarget.children.item(0).classList.contains('job-with-error')) {
|
||||
let target = jQuery(e.currentTarget);
|
||||
let siblings = target.nextAll().toArray();
|
||||
let top = parseInt(target.css('top'), 10);
|
||||
for (let i = 0; i < siblings.length; i++) {
|
||||
let sibling = siblings[i];
|
||||
let siblingTop = parseInt(jQuery(sibling).css('top'), 10);
|
||||
if (siblingTop === top + ROW_HEIGHT) {
|
||||
jQuery(sibling.children).removeClass('hovered');
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private setRowWithErrorClass(hash: { [index: number]: { [id: string]: string; } }, row: number, errorClass: string) {
|
||||
hash[row] = {
|
||||
'_detail_selector': errorClass,
|
||||
'id': errorClass,
|
||||
'jobId': errorClass,
|
||||
'name': errorClass,
|
||||
'targetDatabase': errorClass,
|
||||
'lastRun': errorClass,
|
||||
'nextRun': errorClass,
|
||||
'currentExecutionStatus': errorClass,
|
||||
'lastRunOutcome': errorClass,
|
||||
'previousRuns': errorClass
|
||||
};
|
||||
return hash;
|
||||
}
|
||||
|
||||
private addToStyleHash(row: number, start: boolean, map: any, columnName: string) {
|
||||
let hash: {
|
||||
[index: number]: {
|
||||
[id: string]: string;
|
||||
}
|
||||
} = {};
|
||||
hash = this.setRowWithErrorClass(hash, row, 'job-with-error');
|
||||
hash = this.setRowWithErrorClass(hash, row + 1, 'error-row');
|
||||
if (start) {
|
||||
if (map['start']) {
|
||||
map['start'].push(['error-row' + row.toString(), hash]);
|
||||
} else {
|
||||
map['start'] = [['error-row' + row.toString(), hash]];
|
||||
}
|
||||
} else {
|
||||
if (map[columnName]) {
|
||||
map[columnName].push(['error-row' + row.toString(), hash]);
|
||||
} else {
|
||||
map[columnName] = [['error-row' + row.toString(), hash]];
|
||||
}
|
||||
}
|
||||
this._table.grid.setCellCssStyles('error-row' + row.toString(), hash);
|
||||
}
|
||||
|
||||
private addToErrorStyleHash(row: number, start: boolean, map: any, columnName: string) {
|
||||
let hash: {
|
||||
[index: number]: {
|
||||
[id: string]: string;
|
||||
}
|
||||
} = {};
|
||||
hash = this.setRowWithErrorClass(hash, row, 'job-with-error');
|
||||
hash = this.setRowWithErrorClass(hash, row + 1, 'notebook-error-row');
|
||||
if (start) {
|
||||
if (map['start']) {
|
||||
map['start'].push(['notebook-error-row' + row.toString(), hash]);
|
||||
} else {
|
||||
map['start'] = [['notebook-error-row' + row.toString(), hash]];
|
||||
}
|
||||
} else {
|
||||
if (map[columnName]) {
|
||||
map[columnName].push(['notebook-error-row' + row.toString(), hash]);
|
||||
} else {
|
||||
map[columnName] = [['notebook-error-row' + row.toString(), hash]];
|
||||
}
|
||||
}
|
||||
this._table.grid.setCellCssStyles('notebook-error-row' + row.toString(), hash);
|
||||
}
|
||||
|
||||
private renderName(row, cell, value, columnDef, dataContext) {
|
||||
let resultIndicatorClass: string;
|
||||
switch (dataContext.lastRunOutcome) {
|
||||
case ('Succeeded'):
|
||||
resultIndicatorClass = 'jobview-jobnameindicatorsuccess';
|
||||
break;
|
||||
case ('Failed'):
|
||||
resultIndicatorClass = 'jobview-jobnameindicatorfailure';
|
||||
break;
|
||||
case ('Cancelled'):
|
||||
resultIndicatorClass = 'jobview-jobnameindicatorcancel';
|
||||
break;
|
||||
case ('Status Unknown'):
|
||||
resultIndicatorClass = 'jobview-jobnameindicatorunknown';
|
||||
break;
|
||||
case ('Notebook Error'):
|
||||
resultIndicatorClass = 'jobview-jobnameindicatorcancel';
|
||||
break;
|
||||
default:
|
||||
resultIndicatorClass = 'jobview-jobnameindicatorfailure';
|
||||
break;
|
||||
}
|
||||
|
||||
return '<table class="jobview-jobnametable"><tr class="jobview-jobnamerow">' +
|
||||
'<td nowrap class=' + resultIndicatorClass + '></td>' +
|
||||
'<td nowrap class="jobview-jobnametext">' + escape(dataContext.name) + '</td>' +
|
||||
'</tr></table>';
|
||||
}
|
||||
|
||||
private renderChartsPostHistory(row, cell, value, columnDef, dataContext) {
|
||||
let runChart = this._notebookCacheObject.getRunChart(dataContext.id);
|
||||
if (runChart && runChart.length > 0) {
|
||||
return `<table class="jobprevruns" id="${dataContext.id}">
|
||||
<tr>
|
||||
<td>${runChart[0] ? runChart[0] : '<div class="bar0"></div>'}</td>
|
||||
<td>${runChart[1] ? runChart[1] : '<div class="bar1"></div>'}</td>
|
||||
<td>${runChart[2] ? runChart[2] : '<div class="bar2"></div>'}</td>
|
||||
<td>${runChart[3] ? runChart[3] : '<div class="bar3"></div>'}</td>
|
||||
<td>${runChart[4] ? runChart[4] : '<div class="bar4"></div>'}</td>
|
||||
</tr>
|
||||
</table>`;
|
||||
} else {
|
||||
return `<table class="jobprevruns" id="${dataContext.id}">
|
||||
<tr>
|
||||
<td><div class="bar0"></div></td>
|
||||
<td><div class="bar1"></div></td>
|
||||
<td><div class="bar2"></div></td>
|
||||
<td><div class="bar3"></div></td>
|
||||
<td><div class="bar4"></div></td>
|
||||
</tr>
|
||||
</table>`;
|
||||
}
|
||||
}
|
||||
|
||||
private expandJobRowDetails(rowIdx: number, message?: string): void {
|
||||
let item = this.dataView.getItemByIdx(rowIdx);
|
||||
item.message = this._agentViewComponent.expandedNotebook.get(item.notebookId);
|
||||
this.rowDetail.applyTemplateNewLineHeight(item, true);
|
||||
}
|
||||
|
||||
private async loadJobHistories() {
|
||||
if (this.notebooks) {
|
||||
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];
|
||||
let passing = separatedJobs[1];
|
||||
Promise.all([this.curateJobHistory(failing, ownerUri), this.curateJobHistory(passing, ownerUri)]);
|
||||
}
|
||||
}
|
||||
|
||||
private separateFailingJobs(): azdata.AgentNotebookInfo[][] {
|
||||
let failing = [];
|
||||
let nonFailing = [];
|
||||
for (let i = 0; i < this.notebooks.length; i++) {
|
||||
if (this.notebooks[i].lastRunOutcome === 0) {
|
||||
failing.push(this.notebooks[i]);
|
||||
} else {
|
||||
nonFailing.push(this.notebooks[i]);
|
||||
}
|
||||
}
|
||||
return [failing, nonFailing];
|
||||
}
|
||||
|
||||
private checkPreviousFilters(item): boolean {
|
||||
for (let column in this.filterValueMap) {
|
||||
if (column !== 'start' && this.filterValueMap[column][0].length > 0) {
|
||||
if (!_.contains(this.filterValueMap[column][0], item[JobManagementUtilities.convertColNameToField(column)])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private isErrorRow(cell: HTMLElement) {
|
||||
return cell.classList.contains('error-row') || cell.classList.contains('notebook-error-row');
|
||||
}
|
||||
|
||||
private getNotebook(args: Slick.OnClickEventArgs<any>): azdata.AgentNotebookInfo {
|
||||
let row = args.row;
|
||||
let notebookName: string;
|
||||
let cell = args.grid.getCellNode(row, 1);
|
||||
if (this.isErrorRow(cell)) {
|
||||
notebookName = args.grid.getCellNode(row - 1, 1).innerText.trim();
|
||||
} else {
|
||||
notebookName = cell.innerText.trim();
|
||||
}
|
||||
let notebook = this.notebooks.filter(job => job.name === notebookName)[0];
|
||||
return notebook;
|
||||
}
|
||||
|
||||
private async curateJobHistory(notebooks: azdata.AgentNotebookInfo[], ownerUri: string) {
|
||||
const self = this;
|
||||
for (let notebook of notebooks) {
|
||||
let result = await this._jobManagementService.getNotebookHistory(ownerUri, notebook.jobId, notebook.name, notebook.targetDatabase);
|
||||
if (result) {
|
||||
self.jobSteps[notebook.jobId] = result.steps ? result.steps : [];
|
||||
self.jobSchedules[notebook.jobId] = result.schedules ? result.schedules : [];
|
||||
self.notebookHistories[notebook.jobId] = result.histories ? result.histories : [];
|
||||
self._notebookCacheObject.setJobSteps(notebook.jobId, self.jobSteps[notebook.jobId]);
|
||||
self._notebookCacheObject.setNotebookHistory(notebook.jobId, self.notebookHistories[notebook.jobId]);
|
||||
self._notebookCacheObject.setJobSchedules(notebook.jobId, self.jobSchedules[notebook.jobId]);
|
||||
let notebookHistories = self._notebookCacheObject.getNotebookHistory(notebook.jobId);
|
||||
let previousRuns: azdata.AgentNotebookHistoryInfo[];
|
||||
if (notebookHistories.length >= 5) {
|
||||
previousRuns = notebookHistories.slice(notebookHistories.length - 5, notebookHistories.length);
|
||||
} else {
|
||||
previousRuns = notebookHistories;
|
||||
}
|
||||
|
||||
if (self._agentViewComponent.expandedNotebook.has(notebook.jobId)) {
|
||||
let lastJobHistory = notebookHistories[notebookHistories.length - 1];
|
||||
let item = self.dataView.getItemById('notebook' + notebook.jobId + '.error');
|
||||
let noStepsMessage = nls.localize('notebooksView.noSteps', "No Steps available for this job.");
|
||||
let errorMessage = lastJobHistory ? lastJobHistory.message : noStepsMessage;
|
||||
if (item) {
|
||||
if (notebook.lastRunNotebookError.length === 0) {
|
||||
item['name'] = nls.localize('notebooksView.error', "Error: ") + errorMessage;
|
||||
}
|
||||
else {
|
||||
item['name'] = nls.localize('notebooksView.notebookError', "Notebook Error: ") + notebook.lastRunNotebookError;
|
||||
}
|
||||
self._agentViewComponent.setExpandedNotebook(notebook.jobId, item['name']);
|
||||
self.dataView.updateItem('notebook' + notebook.jobId + '.error', item);
|
||||
}
|
||||
}
|
||||
self.createJobChart('notebook' + notebook.jobId, previousRuns);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private createJobChart(jobId: string, jobHistories: azdata.AgentNotebookHistoryInfo[]): void {
|
||||
let chartHeights = this.getChartHeights(jobHistories);
|
||||
let runCharts = [];
|
||||
for (let i = 0; i < chartHeights.length; i++) {
|
||||
let bgColor = jobHistories[i].runStatus === 0 ? 'red' : 'green';
|
||||
if (jobHistories[i].materializedNotebookErrorInfo !== null && jobHistories[i].materializedNotebookErrorInfo.length > 0) {
|
||||
bgColor = 'orange';
|
||||
}
|
||||
let runGraph = jQuery(`table.jobprevruns#${jobId} > tbody > tr > td > div.bar${i}`);
|
||||
if (runGraph.length > 0) {
|
||||
|
||||
runGraph.css('height', chartHeights[i]);
|
||||
|
||||
runGraph.css('background', bgColor);
|
||||
runGraph.hover((e) => {
|
||||
let currentTarget = e.currentTarget;
|
||||
currentTarget.title = jobHistories[i].runDuration;
|
||||
});
|
||||
runCharts.push(runGraph.get(0).outerHTML);
|
||||
}
|
||||
}
|
||||
if (runCharts.length > 0) {
|
||||
this._notebookCacheObject.setRunChart(jobId, runCharts);
|
||||
}
|
||||
this._cd.detectChanges();
|
||||
}
|
||||
|
||||
// chart height normalization logic
|
||||
private getChartHeights(jobHistories: azdata.AgentJobHistoryInfo[]): string[] {
|
||||
if (!jobHistories || jobHistories.length === 0) {
|
||||
return [];
|
||||
}
|
||||
let maxDuration: number = 0;
|
||||
jobHistories.forEach(history => {
|
||||
let historyDuration = JobManagementUtilities.convertDurationToSeconds(history.runDuration);
|
||||
if (historyDuration > maxDuration) {
|
||||
maxDuration = historyDuration;
|
||||
}
|
||||
});
|
||||
maxDuration = maxDuration === 0 ? 1 : maxDuration;
|
||||
let maxBarHeight: number = 24;
|
||||
let chartHeights = [];
|
||||
let zeroDurationJobCount = 0;
|
||||
for (let i = 0; i < jobHistories.length; i++) {
|
||||
let duration = jobHistories[i].runDuration;
|
||||
let chartHeight = (maxBarHeight * JobManagementUtilities.convertDurationToSeconds(duration)) / maxDuration;
|
||||
chartHeights.push(`${chartHeight}px`);
|
||||
if (chartHeight === 0) {
|
||||
zeroDurationJobCount++;
|
||||
}
|
||||
}
|
||||
// if the durations are all 0 secs, show minimal chart
|
||||
// instead of nothing
|
||||
if (zeroDurationJobCount === jobHistories.length) {
|
||||
return Array(jobHistories.length).fill('5px');
|
||||
} else {
|
||||
return chartHeights;
|
||||
}
|
||||
}
|
||||
|
||||
private expandJobs(start: boolean): void {
|
||||
if (start) {
|
||||
this._agentViewComponent.expandedNotebook = new Map<string, string>();
|
||||
}
|
||||
let expandedJobs = this._agentViewComponent.expandedNotebook;
|
||||
let expansions = 0;
|
||||
for (let i = 0; i < this.notebooks.length; i++) {
|
||||
let notebook = this.notebooks[i];
|
||||
if (notebook.lastRunOutcome === 0 && !expandedJobs.get(notebook.jobId)) {
|
||||
this.expandJobRowDetails(i + expandedJobs.size);
|
||||
this.addToStyleHash(i + expandedJobs.size, start, this.filterStylingMap, undefined);
|
||||
this._agentViewComponent.setExpandedNotebook(notebook.jobId, 'Loading Error...');
|
||||
} else if (notebook.lastRunOutcome === 0 && expandedJobs.get(notebook.jobId)) {
|
||||
this.addToStyleHash(i + expansions, start, this.filterStylingMap, undefined);
|
||||
expansions++;
|
||||
} else if (notebook.lastRunNotebookError !== '' && !expandedJobs.get(notebook.jobId)) {
|
||||
this.expandJobRowDetails(i + expandedJobs.size);
|
||||
this.addToErrorStyleHash(i + expandedJobs.size, start, this.filterStylingMap, undefined);
|
||||
this._agentViewComponent.setExpandedNotebook(notebook.jobId, notebook.lastRunNotebookError);
|
||||
} else if (notebook.lastRunNotebookError !== '' && expandedJobs.get(notebook.jobId)) {
|
||||
this.addToErrorStyleHash(i + expansions, start, this.filterStylingMap, undefined);
|
||||
expansions++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private filter(item: any) {
|
||||
let columns = this._table.grid.getColumns();
|
||||
let value = true;
|
||||
for (let i = 0; i < columns.length; i++) {
|
||||
let col: any = columns[i];
|
||||
let filterValues = col.filterValues;
|
||||
if (filterValues && filterValues.length > 0) {
|
||||
if (item._parent) {
|
||||
value = value && _.contains(filterValues, item._parent[col.field]);
|
||||
} else {
|
||||
value = value && _.contains(filterValues, item[col.field]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
private columnSort(column: string, isAscending: boolean) {
|
||||
let items = this.dataView.getItems();
|
||||
// get error items here and remove them
|
||||
let jobItems = items.filter(x => x._parent === undefined);
|
||||
let errorItems = items.filter(x => x._parent !== undefined);
|
||||
this.sortingStylingMap[column] = items;
|
||||
switch (column) {
|
||||
case ('Name'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => {
|
||||
return item1.name.localeCompare(item2.name);
|
||||
}, isAscending);
|
||||
break;
|
||||
}
|
||||
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'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => this.dateCompare(item1, item2, false), isAscending);
|
||||
break;
|
||||
}
|
||||
case ('Status'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => {
|
||||
return item1.currentExecutionStatus.localeCompare(item2.currentExecutionStatus);
|
||||
}, isAscending);
|
||||
break;
|
||||
}
|
||||
case ('Last Run Outcome'): {
|
||||
this.dataView.setItems(jobItems);
|
||||
// sort the actual jobs
|
||||
this.dataView.sort((item1, item2) => {
|
||||
return item1.lastRunOutcome.localeCompare(item2.lastRunOutcome);
|
||||
}, isAscending);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// insert the errors back again
|
||||
let jobItemsLength = jobItems.length;
|
||||
for (let i = 0; i < jobItemsLength; i++) {
|
||||
let item = jobItems[i];
|
||||
if (item._child) {
|
||||
let child = errorItems.find(error => error === item._child);
|
||||
jobItems.splice(i + 1, 0, child);
|
||||
jobItemsLength++;
|
||||
}
|
||||
}
|
||||
this.dataView.setItems(jobItems);
|
||||
// remove old style
|
||||
if (this.filterStylingMap[column]) {
|
||||
let filterLength = this.filterStylingMap[column].length;
|
||||
for (let i = 0; i < filterLength; i++) {
|
||||
let lastAppliedStyle = this.filterStylingMap[column].pop();
|
||||
this._table.grid.removeCellCssStyles(lastAppliedStyle[0]);
|
||||
}
|
||||
} else {
|
||||
for (let i = 0; i < this.notebooks.length; i++) {
|
||||
this._table.grid.removeCellCssStyles('error-row' + i.toString());
|
||||
this._table.grid.removeCellCssStyles('notebook-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++) {
|
||||
let item = items[i];
|
||||
if (item.lastRunOutcome === 'Failed') {
|
||||
this.addToStyleHash(i, false, this.sortingStylingMap, column);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private dateCompare(item1: any, item2: any, lastRun: boolean): number {
|
||||
let exceptionString = lastRun ? 'Never Run' : 'Not Scheduled';
|
||||
if (item2.lastRun === exceptionString && item1.lastRun !== exceptionString) {
|
||||
return -1;
|
||||
} else if (item1.lastRun === exceptionString && item2.lastRun !== exceptionString) {
|
||||
return 1;
|
||||
} else if (item1.lastRun === exceptionString && item2.lastRun === exceptionString) {
|
||||
return 0;
|
||||
} else {
|
||||
let date1 = new Date(item1.lastRun);
|
||||
let date2 = new Date(item2.lastRun);
|
||||
if (date1 > date2) {
|
||||
return 1;
|
||||
} else if (date1 === date2) {
|
||||
return 0;
|
||||
} else {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private updateTheme(theme: IColorTheme) {
|
||||
let bgColor = theme.getColor(tableBackground);
|
||||
let cellColor = theme.getColor(cellBackground);
|
||||
let borderColor = theme.getColor(cellBorderColor);
|
||||
let headerColumns = jQuery('#agentViewDiv .slick-header-column');
|
||||
let cells = jQuery('.grid-canvas .ui-widget-content.slick-row .slick-cell');
|
||||
let cellDetails = jQuery('#notebooksDiv .dynamic-cell-detail');
|
||||
headerColumns.toArray().forEach(col => {
|
||||
col.style.background = bgColor.toString();
|
||||
});
|
||||
cells.toArray().forEach(cell => {
|
||||
cell.style.background = bgColor.toString();
|
||||
cell.style.border = borderColor ? '1px solid ' + borderColor.toString() : null;
|
||||
});
|
||||
cellDetails.toArray().forEach(cellDetail => {
|
||||
cellDetail.style.background = cellColor.toString();
|
||||
});
|
||||
}
|
||||
|
||||
protected getTableActions(targetObject: JobActionContext): IAction[] {
|
||||
const editAction = this._instantiationService.createInstance(EditJobAction);
|
||||
const editNotebookAction = this._instantiationService.createInstance(EditNotebookJobAction);
|
||||
const runJobAction = this._instantiationService.createInstance(RunJobAction);
|
||||
return [
|
||||
runJobAction,
|
||||
editNotebookAction,
|
||||
editAction,
|
||||
this._instantiationService.createInstance(DeleteNotebookAction)
|
||||
];
|
||||
}
|
||||
|
||||
protected convertStepsToStepInfos(steps: azdata.AgentJobStep[], job: azdata.AgentJobInfo): azdata.AgentJobStepInfo[] {
|
||||
let result = [];
|
||||
steps.forEach(step => {
|
||||
let stepInfo: azdata.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): JobActionContext {
|
||||
let data = this._table.grid.getData() as Slick.DataProvider<IItem>;
|
||||
if (!data || rowIndex >= data.getLength()) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let notebookID = data.getItem(rowIndex).notebookId;
|
||||
if (!notebookID) {
|
||||
// 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) {
|
||||
notebookID = data.getItem(rowIndex - 1).notebookId;
|
||||
}
|
||||
}
|
||||
|
||||
let notebook: azdata.AgentNotebookInfo[] = this.notebooks.filter(job => {
|
||||
return job.jobId === notebookID;
|
||||
});
|
||||
|
||||
if (notebook && notebook.length > 0) {
|
||||
// add steps
|
||||
if (this.jobSteps && this.jobSteps[notebookID]) {
|
||||
let steps = this.jobSteps[notebookID];
|
||||
notebook[0].jobSteps = steps;
|
||||
}
|
||||
|
||||
// add schedules
|
||||
if (this.jobSchedules && this.jobSchedules[notebookID]) {
|
||||
let schedules = this.jobSchedules[notebookID];
|
||||
notebook[0].jobSchedules = schedules;
|
||||
}
|
||||
// add alerts
|
||||
if (this.jobAlerts && this.jobAlerts[notebookID]) {
|
||||
let alerts = this.jobAlerts[notebookID];
|
||||
notebook[0].alerts = alerts;
|
||||
}
|
||||
|
||||
if (notebook[0].jobSteps && notebook[0].jobSchedules && notebook[0].alerts) {
|
||||
return { job: notebook[0], canEdit: true };
|
||||
}
|
||||
return { job: notebook[0], canEdit: false };
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
public async openCreateJobDialog() {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
await this._commandService.executeCommand('agent.openJobDialog', ownerUri);
|
||||
}
|
||||
|
||||
public async openCreateNotebookDialog() {
|
||||
let ownerUri: string = this._commonService.connectionManagementService.connectionInfo.ownerUri;
|
||||
await this._commandService.executeCommand('agent.openNotebookDialog', ownerUri);
|
||||
}
|
||||
}
|
||||