You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@helix.apache.org by jx...@apache.org on 2018/08/18 00:20:02 UTC

[02/14] helix git commit: Support simple job detail view

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 <vx...@linkedin.com>
Authored: Fri Feb 2 16:42:14 2018 -0800
Committer: Vivo Xu <vx...@linkedin.com>
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",