Support simple job detail view
Project: http://git-wip-us.apache.org/repos/asf/helix/repo Commit: http://git-wip-us.apache.org/repos/asf/helix/commit/9da75925 Tree: http://git-wip-us.apache.org/repos/asf/helix/tree/9da75925 Diff: http://git-wip-us.apache.org/repos/asf/helix/diff/9da75925 Branch: refs/heads/master Commit: 9da759256b408dc18e8e9827f99545b252daac73 Parents: b486d78 Author: Vivo Xu <[email protected]> Authored: Fri Feb 2 16:42:14 2018 -0800 Committer: Vivo Xu <[email protected]> Committed: Wed Aug 8 15:30:45 2018 -0700 ---------------------------------------------------------------------- .../job-detail/job-detail.component.html | 10 ++++ .../job-detail/job-detail.component.scss | 0 .../job-detail/job-detail.component.spec.ts | 43 ++++++++++++++ .../workflow/job-detail/job-detail.component.ts | 33 +++++++++++ .../workflow/job-list/job-list.component.html | 62 ++++++++++++++++++++ .../workflow/job-list/job-list.component.scss | 0 .../job-list/job-list.component.spec.ts | 33 +++++++++++ .../app/workflow/job-list/job-list.component.ts | 47 +++++++++++++++ .../app/workflow/shared/job.service.spec.ts | 18 ++++++ .../client/app/workflow/shared/job.service.ts | 17 ++++++ .../app/workflow/shared/workflow.model.ts | 17 +++++- .../workflow/shared/workflow.service.spec.ts | 6 +- .../app/workflow/shared/workflow.service.ts | 2 +- .../workflow-detail.component.html | 49 +--------------- .../workflow-detail.component.ts | 36 ++---------- .../client/app/workflow/workflow.module.ts | 12 +++- helix-front/package.json | 2 +- 17 files changed, 299 insertions(+), 88 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/job-detail/job-detail.component.html ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/job-detail/job-detail.component.html b/helix-front/client/app/workflow/job-detail/job-detail.component.html new file mode 100644 index 0000000..3eb5416 --- /dev/null +++ b/helix-front/client/app/workflow/job-detail/job-detail.component.html @@ -0,0 +1,10 @@ +<mat-tab-group> + <mat-tab label="Job Config"> + <mat-spinner *ngIf="isLoading"></mat-spinner> + <ngx-json-viewer [json]="job?.config"></ngx-json-viewer> + </mat-tab> + <mat-tab label="Job Context"> + <mat-spinner *ngIf="isLoading"></mat-spinner> + <ngx-json-viewer [json]="job?.context"></ngx-json-viewer> + </mat-tab> +</mat-tab-group> http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/job-detail/job-detail.component.scss ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/job-detail/job-detail.component.scss b/helix-front/client/app/workflow/job-detail/job-detail.component.scss new file mode 100644 index 0000000..e69de29 http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/job-detail/job-detail.component.spec.ts ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/job-detail/job-detail.component.spec.ts b/helix-front/client/app/workflow/job-detail/job-detail.component.spec.ts new file mode 100644 index 0000000..b720650 --- /dev/null +++ b/helix-front/client/app/workflow/job-detail/job-detail.component.spec.ts @@ -0,0 +1,43 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NO_ERRORS_SCHEMA } from '@angular/core'; +import { Observable } from 'rxjs/Observable'; + +import { TestingModule } from '../../../testing/testing.module'; +import { JobDetailComponent } from './job-detail.component'; +import { JobService } from '../shared/job.service'; + +describe('JobDetailComponent', () => { + let component: JobDetailComponent; + let fixture: ComponentFixture<JobDetailComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ TestingModule ], + providers: [ + { + provide: JobService, + useValue: { + get: job => Observable.of() + } + } + ], + declarations: [ JobDetailComponent ], + schemas: [ + /* avoid importing modules */ + NO_ERRORS_SCHEMA + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(JobDetailComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/job-detail/job-detail.component.ts ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/job-detail/job-detail.component.ts b/helix-front/client/app/workflow/job-detail/job-detail.component.ts new file mode 100644 index 0000000..97bd890 --- /dev/null +++ b/helix-front/client/app/workflow/job-detail/job-detail.component.ts @@ -0,0 +1,33 @@ +import { Component, OnInit, Input } from '@angular/core'; + +import { Job } from '../shared/workflow.model'; +import { JobService } from '../shared/job.service'; +import { HelperService } from '../../shared/helper.service'; + +@Component({ + selector: 'hi-job-detail', + templateUrl: './job-detail.component.html', + styleUrls: ['./job-detail.component.scss'] +}) +export class JobDetailComponent implements OnInit { + + @Input() + job: Job; + + isLoading = true; + + constructor( + protected service: JobService, + protected helper: HelperService + ) { } + + ngOnInit() { + this.service.get(this.job) + .subscribe( + data => this.isLoading = false, + error => this.helper.showError(error), + () => this.isLoading = false + ) + } + +} http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/job-list/job-list.component.html ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/job-list/job-list.component.html b/helix-front/client/app/workflow/job-list/job-list.component.html new file mode 100644 index 0000000..2592c1f --- /dev/null +++ b/helix-front/client/app/workflow/job-list/job-list.component.html @@ -0,0 +1,62 @@ +<ngx-datatable + #jobsTable + class="material" + [headerHeight]="headerHeight" + [rowHeight]="rowHeight" + columnMode="force" + [footerHeight]="rowHeight" + [rows]="jobs" + selectionType="single" + [sorts]="sorts" + (select)="onSelect($event)" + [messages]="messages" + fxFill> + <ngx-datatable-column + [width]="50" + [resizeable]="false" + [sortable]="false" + [draggable]="false" + [canAutoResize]="false"> + <ng-template let-expanded="expanded" ngx-datatable-cell-template> + <mat-icon>{{ expanded ? 'expand_more' : 'chevron_right' }}</mat-icon> + </ng-template> + </ngx-datatable-column> + <ngx-datatable-column + name="Start Time" + [width]="200" + [resizeable]="false" + [draggable]="false" + [canAutoResize]="false"> + <ng-template let-value="value" ngx-datatable-cell-template> + <span *ngIf="value" [matTooltip]="value | date:'medium'"> + {{ parseTime(value) }} + </span> + <span *ngIf="!value">-</span> + </ng-template> + </ngx-datatable-column> + <ngx-datatable-column name="Job Name" prop="name"> + <ng-template let-row="row" let-value="value" ngx-datatable-cell-template> + <span [matTooltip]="row.rawName"> + ...{{ value }} + </span> + </ng-template> + </ngx-datatable-column> + <ngx-datatable-column + name="State" + [width]="120" + [resizeable]="false" + [draggable]="false" + [canAutoResize]="false"> + <ng-template let-value="value" ngx-datatable-cell-template> + <span *ngIf="value" class="state-default state-{{ value }}"> + {{ value }} + </span> + <span *ngIf="!value" class="state-PENDING">PENDING</span> + </ng-template> + </ngx-datatable-column> + <ngx-datatable-row-detail rowHeight="auto"> + <ng-template let-row="row" ngx-datatable-row-detail-template> + <hi-job-detail [job]="row"></hi-job-detail> + </ng-template> + </ngx-datatable-row-detail> +</ngx-datatable> http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/job-list/job-list.component.scss ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/job-list/job-list.component.scss b/helix-front/client/app/workflow/job-list/job-list.component.scss new file mode 100644 index 0000000..e69de29 http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/job-list/job-list.component.spec.ts ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/job-list/job-list.component.spec.ts b/helix-front/client/app/workflow/job-list/job-list.component.spec.ts new file mode 100644 index 0000000..d9f75ac --- /dev/null +++ b/helix-front/client/app/workflow/job-list/job-list.component.spec.ts @@ -0,0 +1,33 @@ +import { async, ComponentFixture, TestBed } from '@angular/core/testing'; + +import { NO_ERRORS_SCHEMA } from '@angular/core'; + +import { TestingModule } from '../../../testing/testing.module'; +import { JobListComponent } from './job-list.component'; + +describe('JobListComponent', () => { + let component: JobListComponent; + let fixture: ComponentFixture<JobListComponent>; + + beforeEach(async(() => { + TestBed.configureTestingModule({ + imports: [ TestingModule ], + declarations: [ JobListComponent ], + schemas: [ + /* avoid importing modules */ + NO_ERRORS_SCHEMA + ] + }) + .compileComponents(); + })); + + beforeEach(() => { + fixture = TestBed.createComponent(JobListComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/job-list/job-list.component.ts ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/job-list/job-list.component.ts b/helix-front/client/app/workflow/job-list/job-list.component.ts new file mode 100644 index 0000000..ef85de8 --- /dev/null +++ b/helix-front/client/app/workflow/job-list/job-list.component.ts @@ -0,0 +1,47 @@ +import { Component, OnInit, Input, ViewChild } from '@angular/core'; + +import * as moment from 'moment'; + +import { Settings } from '../../core/settings'; +import { Job } from '../shared/workflow.model'; + +@Component({ + selector: 'hi-job-list', + templateUrl: './job-list.component.html', + styleUrls: ['./job-list.component.scss'] +}) +export class JobListComponent implements OnInit { + + @Input() + jobs: Job[]; + + @ViewChild('jobsTable') + table: any; + + rowHeight = Settings.tableRowHeight; + headerHeight = Settings.tableHeaderHeight; + sorts = [ + { prop: 'startTime', dir: 'desc'}, + { prop: 'name', dir: 'asc'} + ]; + messages = { + emptyMessage: 'The list is empty.', + totalMessage: 'total', + selectedMessage: 'selected' + }; + + constructor() { } + + ngOnInit() { + } + + parseTime(rawTime: string): string { + return moment(parseInt(rawTime)).fromNow(); + } + + onSelect({ selected }) { + const row = selected[0]; + + this.table.rowDetail.toggleExpandRow(row); + } +} http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/shared/job.service.spec.ts ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/shared/job.service.spec.ts b/helix-front/client/app/workflow/shared/job.service.spec.ts new file mode 100644 index 0000000..ecc42b6 --- /dev/null +++ b/helix-front/client/app/workflow/shared/job.service.spec.ts @@ -0,0 +1,18 @@ +import { TestBed, inject } from '@angular/core/testing'; + +import { TestingModule } from '../../../testing/testing.module'; + +import { JobService } from './job.service'; + +describe('JobService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [TestingModule], + providers: [JobService] + }); + }); + + it('should be created', inject([JobService], (service: JobService) => { + expect(service).toBeTruthy(); + })); +}); http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/shared/job.service.ts ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/shared/job.service.ts b/helix-front/client/app/workflow/shared/job.service.ts new file mode 100644 index 0000000..6f044c9 --- /dev/null +++ b/helix-front/client/app/workflow/shared/job.service.ts @@ -0,0 +1,17 @@ +import { Injectable } from '@angular/core'; + +import { Job } from './workflow.model'; +import { HelixService } from '../../core/helix.service'; + +@Injectable() +export class JobService extends HelixService { + + public get(job: Job) { + return this + .request(`/clusters/${ job.clusterName }/workflows/${ job.workflowName }/jobs/${ job.rawName }`) + .map(data => { + job.config = data.JobConfig; + job.context = data.JobContext; + }); + } +} http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/shared/workflow.model.ts ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/shared/workflow.model.ts b/helix-front/client/app/workflow/shared/workflow.model.ts index 363c8a7..4a1e42f 100644 --- a/helix-front/client/app/workflow/shared/workflow.model.ts +++ b/helix-front/client/app/workflow/shared/workflow.model.ts @@ -11,9 +11,17 @@ export class Job { readonly state: string; readonly parents: string[]; + readonly workflowName: string; + readonly clusterName: string; + + // will load later + config: any; + context: any; + constructor( rawName: string, workflowName: string, + clusterName: string, startTime: string, state: string, parents: string[] @@ -21,6 +29,8 @@ export class Job { this.rawName = rawName; // try to reduce the name this.name = _.replace(rawName, workflowName + '_', ''); + this.workflowName = workflowName; + this.clusterName = clusterName; this.startTime = startTime; this.state = state; // try to reduce parent names @@ -30,22 +40,24 @@ export class Job { export class Workflow { readonly name: string; + readonly clusterName: string; readonly config: any; readonly jobs: Job[]; readonly context: any; readonly json: any; get isJobQueue(): boolean { - return this.config && this.config.IsJobQueue.toLowerCase() == 'true'; + return this.config && this.config.IsJobQueue && this.config.IsJobQueue.toLowerCase() == 'true'; } get state(): string { return this.context.STATE || 'NOT STARTED'; } - constructor(obj: any) { + constructor(obj: any, clusterName: string) { this.json = obj; this.name = obj.id; + this.clusterName = clusterName; this.config = obj.WorkflowConfig; this.context = obj.WorkflowContext; this.jobs = this.parseJobs(obj.Jobs, obj.ParentJobs); @@ -58,6 +70,7 @@ export class Workflow { result.push(new Job( jobName, this.name, + this.clusterName, _.get(this.context, ['StartTime', jobName]), _.get(this.context, ['JOB_STATES', jobName]), parents[jobName] http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/shared/workflow.service.spec.ts ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/shared/workflow.service.spec.ts b/helix-front/client/app/workflow/shared/workflow.service.spec.ts index eed01ca..e065079 100644 --- a/helix-front/client/app/workflow/shared/workflow.service.spec.ts +++ b/helix-front/client/app/workflow/shared/workflow.service.spec.ts @@ -1,13 +1,13 @@ import { TestBed, inject } from '@angular/core/testing'; -import { HttpModule } from '@angular/http'; -import { RouterTestingModule } from '@angular/router/testing'; + +import { TestingModule } from '../../../testing/testing.module'; import { WorkflowService } from './workflow.service'; describe('WorkflowService', () => { beforeEach(() => { TestBed.configureTestingModule({ - imports: [HttpModule, RouterTestingModule], + imports: [TestingModule], providers: [WorkflowService] }); }); http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/shared/workflow.service.ts ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/shared/workflow.service.ts b/helix-front/client/app/workflow/shared/workflow.service.ts index 38cec74..9be5337 100644 --- a/helix-front/client/app/workflow/shared/workflow.service.ts +++ b/helix-front/client/app/workflow/shared/workflow.service.ts @@ -15,6 +15,6 @@ export class WorkflowService extends HelixService { public get(clusterName: string, workflowName: string) { return this .request(`/clusters/${ clusterName }/workflows/${ workflowName }`) - .map(data => new Workflow(data)); + .map(data => new Workflow(data, clusterName)); } } http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/workflow-detail/workflow-detail.component.html ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/workflow-detail/workflow-detail.component.html b/helix-front/client/app/workflow/workflow-detail/workflow-detail.component.html index 5a940d3..276e116 100644 --- a/helix-front/client/app/workflow/workflow-detail/workflow-detail.component.html +++ b/helix-front/client/app/workflow/workflow-detail/workflow-detail.component.html @@ -31,54 +31,7 @@ </mat-button-toggle-group> <section class="viewer" [ngSwitch]="group.value" fxLayout="column" fxLayoutAlign="center center" fxFill> <hi-workflow-dag *ngSwitchCase="'graph'" [workflow]="workflow"></hi-workflow-dag> - <ngx-datatable - *ngSwitchCase="'list'" - #jobsTable - class="material" - [headerHeight]="headerHeight" - [rowHeight]="rowHeight" - columnMode="force" - [footerHeight]="rowHeight" - [rows]="workflow.jobs" - selectionType="single" - [sorts]="sorts" - (select)="onSelect($event)" - [messages]="messages" - fxFill> - <ngx-datatable-column - name="Start Time" - [width]="200" - [resizeable]="false" - [draggable]="false" - [canAutoResize]="false"> - <ng-template let-value="value" ngx-datatable-cell-template> - <span *ngIf="value" [matTooltip]="value | date:'medium'"> - {{ parseTime(value) }} - </span> - <span *ngIf="!value">-</span> - </ng-template> - </ngx-datatable-column> - <ngx-datatable-column name="Job Name" prop="name"> - <ng-template let-row="row" let-value="value" ngx-datatable-cell-template> - <span [matTooltip]="row.rawName"> - ...{{ value }} - </span> - </ng-template> - </ngx-datatable-column> - <ngx-datatable-column - name="State" - [width]="120" - [resizeable]="false" - [draggable]="false" - [canAutoResize]="false"> - <ng-template let-value="value" ngx-datatable-cell-template> - <span *ngIf="value" class="state-default state-{{ value }}"> - {{ value }} - </span> - <span *ngIf="!value" class="state-PENDING">PENDING</span> - </ng-template> - </ngx-datatable-column> - </ngx-datatable> + <hi-job-list *ngSwitchCase="'list'" [jobs]="workflow.jobs" fxFill></hi-job-list> <ngx-json-viewer *ngSwitchCase="'json'" [json]="workflow.json" fxFill></ngx-json-viewer> </section> </section> http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/workflow-detail/workflow-detail.component.ts ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/workflow-detail/workflow-detail.component.ts b/helix-front/client/app/workflow/workflow-detail/workflow-detail.component.ts index f1f04ad..a8adcfa 100644 --- a/helix-front/client/app/workflow/workflow-detail/workflow-detail.component.ts +++ b/helix-front/client/app/workflow/workflow-detail/workflow-detail.component.ts @@ -1,13 +1,9 @@ import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; -import * as moment from 'moment'; -import * as shape from 'd3-shape'; -import * as _ from 'lodash'; - -import { Settings } from '../../core/settings'; -import { Workflow, Job } from '../shared/workflow.model'; +import { Workflow } from '../shared/workflow.model'; import { WorkflowService } from '../shared/workflow.service'; +import { HelperService } from '../../shared/helper.service'; @Component({ selector: 'hi-workflow-detail', @@ -20,21 +16,10 @@ export class WorkflowDetailComponent implements OnInit { workflow: Workflow; clusterName: string; - rowHeight = Settings.tableRowHeight; - headerHeight = Settings.tableHeaderHeight; - sorts = [ - { prop: 'startTime', dir: 'desc'}, - { prop: 'name', dir: 'asc'} - ]; - messages = { - emptyMessage: 'The list is empty.', - totalMessage: 'total', - selectedMessage: 'selected' - }; - constructor( - private route: ActivatedRoute, - private service: WorkflowService + protected route: ActivatedRoute, + protected service: WorkflowService, + protected helper: HelperService ) { } ngOnInit() { @@ -47,17 +32,8 @@ export class WorkflowDetailComponent implements OnInit { ) .subscribe( workflow => this.workflow = workflow, - error => console.log(error), + error => this.helper.showError(error), () => this.isLoading = false ); } - - parseTime(rawTime: string): string { - return moment(parseInt(rawTime)).fromNow(); - } - - onSelect({ selected }) { - const row = selected[0]; - // this.table.rowDetail.toggleExpandRow(row); - } } http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/client/app/workflow/workflow.module.ts ---------------------------------------------------------------------- diff --git a/helix-front/client/app/workflow/workflow.module.ts b/helix-front/client/app/workflow/workflow.module.ts index 7ff9868..73f3698 100644 --- a/helix-front/client/app/workflow/workflow.module.ts +++ b/helix-front/client/app/workflow/workflow.module.ts @@ -3,13 +3,16 @@ import { CommonModule } from '@angular/common'; import { NgxDatatableModule } from '@swimlane/ngx-datatable'; import { NgxChartsModule } from '@swimlane/ngx-charts'; -import { NgxGraphModule } from '@swimlane/ngx-graph'; +import { NgxGraphModule } from 'ngx-dag'; import { WorkflowListComponent } from './workflow-list/workflow-list.component'; import { WorkflowService } from './shared/workflow.service'; +import { JobService } from './shared/job.service'; import { SharedModule } from '../shared/shared.module'; import { WorkflowDetailComponent } from './workflow-detail/workflow-detail.component'; import { WorkflowDagComponent } from './workflow-dag/workflow-dag.component'; +import { JobListComponent } from './job-list/job-list.component'; +import { JobDetailComponent } from './job-detail/job-detail.component'; @NgModule({ imports: [ @@ -20,12 +23,15 @@ import { WorkflowDagComponent } from './workflow-dag/workflow-dag.component'; NgxGraphModule ], providers: [ - WorkflowService + WorkflowService, + JobService ], declarations: [ WorkflowListComponent, WorkflowDetailComponent, - WorkflowDagComponent + WorkflowDagComponent, + JobListComponent, + JobDetailComponent ] }) export class WorkflowModule { } http://git-wip-us.apache.org/repos/asf/helix/blob/9da75925/helix-front/package.json ---------------------------------------------------------------------- diff --git a/helix-front/package.json b/helix-front/package.json index 8c8d1b4..e611637 100644 --- a/helix-front/package.json +++ b/helix-front/package.json @@ -35,7 +35,6 @@ "@angular/router": "^5.1.1", "@swimlane/ngx-charts": "^7.0.1", "@swimlane/ngx-datatable": "^11.1.7", - "@swimlane/ngx-graph": "^4.0.2", "angulartics2": "^2.2.1", "body-parser": "^1.17.2", "core-js": "^2.4.1", @@ -48,6 +47,7 @@ "moment": "^2.22.2", "morgan": "^1.8.2", "ngx-clipboard": "^9.0.0", + "ngx-dag": "0.0.2", "ngx-json-viewer": "^2.3.0", "node-sass": "4.5.3", "request": "^2.81.0",
