Skip to content

Commit c53e742

Browse files
Merge pull request #136 from delphi-hub/feature/instanceDetails
WIP: Feature/instance details
2 parents 633d5fe + b91fb83 commit c53e742

19 files changed

+233
-166
lines changed

build.sbt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ name := "delphi-management"
55
organization := "de.upb"
66

77

8-
version := "0.9.5"
8+
version := "1.0.0-SNAPSHOT"
99

1010

1111
scalaVersion := "2.12.4"

client/src/app/app.module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,5 +55,5 @@ import { BASE_PATH, AUTH } from './api/variables';
5555
export class AppModule {
5656
}
5757
export function jwtGetter() {
58-
return localStorage.getItem(TOKEN_IDENT)
58+
return localStorage.getItem(TOKEN_IDENT);
5959
}

client/src/app/dashboard/dashboard-routing.module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ const dashboardRoutes: Routes = [
7575
component: GraphViewComponent
7676
},
7777
{
78-
path: 'instanceDetails',
78+
path: 'instanceDetails/:instanceId',
7979
component: InstanceDetailsComponent
8080
}
8181
]

client/src/app/dashboard/dashboard.module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,8 @@ import { LabelDialogComponent } from './label-dialog/label-dialog.component';
7878
DeleteDialogComponent,
7979
AddDialogComponent,
8080
InstanceDetailsComponent,
81-
TableOverviewComponent,
8281
InfoCenterComponent,
82+
TableOverviewComponent,
8383
LabelDialogComponent
8484
],
8585
entryComponents: [

client/src/app/dashboard/graph-view/graph-view/graph-view.component.spec.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import { HttpClientTestingModule } from '@angular/common/http/testing';
77
import { ConnectDialogComponent } from '../connect-dialog/connect-dialog.component';
88
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
99
import { MaterialModule } from 'src/app/material-module/material.module';
10+
import { RouterTestingModule } from '@angular/router/testing';
1011

1112
describe('GraphViewComponent', () => {
1213
let component: GraphViewComponent;
@@ -15,7 +16,7 @@ describe('GraphViewComponent', () => {
1516
beforeEach(async(() => {
1617
TestBed.configureTestingModule({
1718
declarations: [GraphViewComponent],
18-
imports: [HttpClientTestingModule, HttpClientModule, ApiModule, MaterialModule]
19+
imports: [HttpClientTestingModule, HttpClientModule, ApiModule, MaterialModule, RouterTestingModule]
1920
})
2021
.compileComponents();
2122

client/src/app/dashboard/graph-view/graph-view/graph-view.component.ts

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {Component, ElementRef, OnInit, ViewChild, OnDestroy} from '@angular/core';
1+
import {Component, ElementRef, OnInit, ViewChild, OnDestroy, Input} from '@angular/core';
22
import * as cytoscape from 'cytoscape';
33
import edgehandles from 'cytoscape-edgehandles';
44
import {GraphViewService, ElementUpdate} from '../graph-view.service';
@@ -9,6 +9,7 @@ import { ConnectDialogComponent } from '../connect-dialog/connect-dialog.compone
99
import { ComponentTypeEnum, ComponentType } from 'src/app/model/models/instance';
1010
import { GraphConfig } from '../GraphConfig';
1111
import { Subscription } from 'rxjs';
12+
import { Router } from '@angular/router';
1213

1314
@Component({
1415
selector: 'app-graph-view',
@@ -17,21 +18,23 @@ import { Subscription } from 'rxjs';
1718
})
1819
export class GraphViewComponent implements OnInit, OnDestroy {
1920
@ViewChild('cy') cyDiv: ElementRef;
21+
22+
@Input() subnetElementId = -1;
23+
2024
private cy: cytoscape.Core;
2125
private config: GraphConfig;
2226
private elementSubscription: Subscription;
2327
private elementRemoveSubscription: Subscription;
2428

25-
constructor(private graphViewService: GraphViewService, public dialog: MatDialog) {
26-
27-
}
29+
constructor(private graphViewService: GraphViewService, public dialog: MatDialog, private router: Router) {}
2830

2931

3032
ngOnInit() {
3133

3234
this.configureCytoscape();
3335
this.addEdgeDragListener();
3436
this.addChangeListener();
37+
this.addClickListener();
3538
}
3639

3740
ngOnDestroy() {
@@ -40,6 +43,23 @@ export class GraphViewComponent implements OnInit, OnDestroy {
4043
this.elementRemoveSubscription.unsubscribe();
4144
}
4245

46+
private addClickListener() {
47+
this.cy.nodes().on('click', (e) => {
48+
const clickedNode: cytoscape.NodeSingular = e.target;
49+
const instanceId = clickedNode.data('id');
50+
this.router.navigate(['/dashboard/instanceDetails/' + instanceId]);
51+
});
52+
}
53+
54+
private applySubnetFilter() {
55+
if (this.subnetElementId !== -1) {
56+
const subentElement = this.cy.getElementById('' + this.subnetElementId);
57+
const neighborhood = subentElement.neighborhood().add(subentElement);
58+
const elesToRemove = this.cy.elements().difference(neighborhood);
59+
this.cy.remove(elesToRemove);
60+
}
61+
}
62+
4363
/**
4464
* Adds listeners to the graphViewService and describe how the
4565
* view should be updated if it receives any changes / updates.
@@ -63,8 +83,10 @@ export class GraphViewComponent implements OnInit, OnDestroy {
6383
default: return 'orange';
6484
}
6585
});
86+
this.applySubnetFilter();
6687
const layout = this.cy.layout(this.config.layout);
6788
layout.run();
89+
this.addClickListener();
6890
}
6991
});
7092

@@ -183,7 +205,8 @@ export class GraphViewComponent implements OnInit, OnDestroy {
183205
if (!Object.getPrototypeOf(this.cy).edgehandles) {
184206
cytoscape.use(edgehandles);
185207
}
186-
187-
(this.cy as any).edgehandles(this.config.edgeDragConfig);
208+
if (this.subnetElementId === -1) {
209+
(this.cy as any).edgehandles(this.config.edgeDragConfig);
210+
}
188211
}
189212
}

client/src/app/dashboard/info-center/info-center-datasource.ts

Lines changed: 88 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import { DataSource } from '@angular/cdk/collections';
22
import { MatPaginator, MatSort } from '@angular/material';
3-
import { map } from 'rxjs/operators';
4-
import { Observable, of as observableOf, merge } from 'rxjs';
3+
import { Observable, BehaviorSubject, Subscription } from 'rxjs';
4+
import { SocketService } from 'src/app/api/api/socket.service';
5+
import { Instance } from 'src/app/model/models/instance';
6+
import { DatePipe } from '@angular/common';
7+
import { EventTypeEnum } from 'src/app/model/models/socketMessage';
8+
import { StoreService } from 'src/app/model/store.service';
59

610
export interface InfoCenterItem {
711
instanceId: number;
@@ -11,18 +15,71 @@ export interface InfoCenterItem {
1115
details: string;
1216
}
1317

14-
const DATA: InfoCenterItem[] = [];
15-
1618
/**
1719
* Data source for the InfoCenter view. This class should
1820
* encapsulate all logic for fetching and manipulating the displayed data
1921
* (including sorting, pagination, and filtering).
2022
*/
2123
export class InfoCenterDataSource extends DataSource<InfoCenterItem> {
22-
data: InfoCenterItem[] = DATA;
24+
private infoCenterSubject: BehaviorSubject<InfoCenterItem[]>;
25+
26+
private instanceAddedSubscription: Subscription;
27+
private instanceChangedSubscription: Subscription;
28+
private instanceRemovedSubscription: Subscription;
29+
private data: InfoCenterItem[];
30+
public numberEvents = 0;
31+
private instance: Instance;
2332

24-
constructor(private paginator: MatPaginator, private sort: MatSort) {
33+
constructor(private storeService: StoreService, private socketService: SocketService,
34+
private paginator: MatPaginator, private sort: MatSort, private compType: string, private instanceId: string) {
2535
super();
36+
this.data = [];
37+
if (this.instanceId) {
38+
this.instance = this.storeService.getState().instances[this.instanceId];
39+
if (!this.instance) {
40+
this.instanceId = null;
41+
}
42+
}
43+
this.infoCenterSubject = new BehaviorSubject<InfoCenterItem[]>([]);
44+
45+
this.paginator.page.subscribe(() => { this.infoCenterSubject.next(this.getPagedData()); });
46+
this.sort.sortChange.subscribe(() => {this.data = this.getSortedData(this.data); this.infoCenterSubject.next(this.getPagedData()); });
47+
48+
this.instanceAddedSubscription = this.socketService.subscribeForEvent<Instance>(EventTypeEnum.InstanceAddedEvent).
49+
subscribe((newInstance: Instance) => {
50+
if (this.applyFilter(newInstance)) {
51+
const newEntry = this.transformEventToNotificaton(newInstance, 'new Instance added', 'add_circle');
52+
this.applyUpdate(newEntry);
53+
}
54+
});
55+
56+
this.instanceRemovedSubscription = this.socketService.subscribeForEvent<Instance>(EventTypeEnum.InstanceRemovedEvent).
57+
subscribe((removeInstance: Instance) => {
58+
if (this.applyFilter(removeInstance)) {
59+
const newEntry = this.transformEventToNotificaton(removeInstance, 'Instance removed', 'delete_sweep');
60+
this.applyUpdate(newEntry);
61+
}
62+
});
63+
64+
this.instanceChangedSubscription = this.socketService.subscribeForEvent<Instance>(EventTypeEnum.StateChangedEvent).
65+
subscribe((changeInstance: Instance) => {
66+
if (this.applyFilter(changeInstance)) {
67+
const newEntry = this.transformEventToNotificaton(changeInstance, 'Instance changed', 'link');
68+
this.applyUpdate(newEntry);
69+
}
70+
});
71+
}
72+
73+
applyFilter(instance: Instance): boolean {
74+
if (!this.instanceId && !this.compType) {
75+
return true;
76+
} else {
77+
if (this.instanceId) {
78+
return instance.id === this.instance.id;
79+
} else {
80+
return instance.componentType === this.compType;
81+
}
82+
}
2683
}
2784

2885
/**
@@ -31,34 +88,33 @@ export class InfoCenterDataSource extends DataSource<InfoCenterItem> {
3188
* @returns A stream of the items to be rendered.
3289
*/
3390
connect(): Observable<InfoCenterItem[]> {
34-
// Combine everything that affects the rendered data into one update
35-
// stream for the data-table to consume.
36-
const dataMutations = [
37-
observableOf(this.data),
38-
this.paginator.page,
39-
];
40-
41-
// Set the paginator's length
42-
this.paginator.length = this.data.length;
43-
44-
return merge(...dataMutations).pipe(map(() => {
45-
return this.getPagedData(this.getSortedData([...this.data]));
46-
}));
91+
return this.infoCenterSubject.asObservable();
92+
}
93+
94+
private applyUpdate(newEntry: InfoCenterItem) {
95+
this.data = this.getSortedData([newEntry, ...this.data]);
96+
this.numberEvents = this.data.length;
97+
this.infoCenterSubject.next(this.getPagedData());
4798
}
4899

49100
/**
50101
* Called when the table is being destroyed. Use this function, to clean up
51102
* any open connections or free any held resources that were set up during connect.
52103
*/
53-
disconnect() {}
104+
disconnect() {
105+
this.instanceAddedSubscription.unsubscribe();
106+
this.instanceChangedSubscription.unsubscribe();
107+
this.instanceRemovedSubscription.unsubscribe();
108+
this.infoCenterSubject.complete();
109+
}
54110

55111
/**
56112
* Paginate the data (client-side). If you're using server-side pagination,
57113
* this would be replaced by requesting the appropriate data from the server.
58114
*/
59-
private getPagedData(data: InfoCenterItem[]) {
115+
private getPagedData() {
60116
const startIndex = this.paginator.pageIndex * this.paginator.pageSize;
61-
return data.splice(startIndex, this.paginator.pageSize);
117+
return [...this.data].splice(startIndex, this.paginator.pageSize);
62118
}
63119

64120
/**
@@ -73,15 +129,21 @@ export class InfoCenterDataSource extends DataSource<InfoCenterItem> {
73129
return data.sort((a, b) => {
74130
const isAsc = this.sort.direction === 'asc';
75131
switch (this.sort.active) {
76-
case 'name': return compare(a.notifName, b.notifName, isAsc);
77-
case 'id': return compare(+a.dateTime, +b.dateTime, isAsc);
132+
case 'notifName': return compare(a.notifName, b.notifName, isAsc);
133+
case 'dateTime': return compare(+a.dateTime, +b.dateTime, isAsc);
134+
case 'details': return compare(a.details, b.details, isAsc);
78135
default: return 0;
79136
}
80137
});
81138
}
82-
public add(element: InfoCenterItem) {
83-
this.data.push(element);
139+
140+
private transformEventToNotificaton(instance: Instance, notifName: string, type: string): InfoCenterItem {
141+
const datePipe = new DatePipe('en-US');
142+
const actualDate = datePipe.transform(Date.now(), 'dd/MM/yyyy hh:mm:ss:SSS');
143+
return {instanceId: instance.id, type: type,
144+
notifName: notifName, dateTime: actualDate, details: instance.name};
84145
}
146+
85147
}
86148

87149
/** Simple sort comparator for example ID/Name columns (for client-side sorting). */

client/src/app/dashboard/info-center/info-center.component.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ <h4>Information Center</h4>
3131
</mat-table>
3232

3333
<mat-paginator #paginator
34-
[length]="dataSource.data.length"
34+
[length]="dataSource.numberEvents"
3535
[pageIndex]="0"
3636
[pageSize]="5"
3737
[pageSizeOptions]="[5, 10, 25, 100]">
Lines changed: 9 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
import {ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core';
1+
import {Component, ViewChild, Input, AfterViewInit, OnInit} from '@angular/core';
22
import {MatPaginator, MatSort, MatTable} from '@angular/material';
33
import {InfoCenterDataSource, InfoCenterItem } from './info-center-datasource';
44
import {SocketService} from '../../api/api/socket.service';
5-
import {DatePipe} from '@angular/common';
6-
import {EventTypeEnum} from '../../model/models/socketMessage';
7-
import {Instance} from '../../model/models/instance';
5+
import { StoreService } from 'src/app/model/store.service';
6+
87

98
@Component({
109
selector: 'app-info-center',
@@ -14,46 +13,19 @@ import {Instance} from '../../model/models/instance';
1413
export class InfoCenterComponent implements OnInit {
1514
@ViewChild(MatPaginator) paginator: MatPaginator;
1615
@ViewChild(MatSort) sort: MatSort;
17-
@ViewChild(MatTable) table: MatTable<any>;
16+
@ViewChild(MatTable) table: MatTable<InfoCenterItem>;
17+
@Input()compType: string;
18+
@Input()instanceId: string;
1819
dataSource: InfoCenterDataSource;
1920

2021
/** Columns displayed in the table. Columns IDs can be added, removed, or reordered. */
2122
displayedColumns = ['type', 'notifName', 'dateTime', 'details'];
2223

23-
constructor(private socketService: SocketService, private changeDetectorRefs: ChangeDetectorRef) {}
24+
constructor(private socketService: SocketService, private storeService: StoreService) {}
2425

2526
ngOnInit() {
26-
this.dataSource = new InfoCenterDataSource(this.paginator, this.sort);
27-
28-
this.socketService.subscribeForEvent<Instance>(EventTypeEnum.InstanceAddedEvent).subscribe((newInstance: Instance) => {
29-
const newEntry = this.transformEventToNotificaton(newInstance, 'new Instance added', 'add_circle');
30-
this.applyUpdate(newEntry);
31-
});
32-
33-
this.socketService.subscribeForEvent<Instance>(EventTypeEnum.InstanceRemovedEvent).subscribe((removeInstance: Instance) => {
34-
const newEntry = this.transformEventToNotificaton(removeInstance, 'Instance removed', 'delete_sweep');
35-
this.applyUpdate(newEntry);
36-
});
37-
38-
this.socketService.subscribeForEvent<Instance>(EventTypeEnum.StateChangedEvent).subscribe((changeInstance: Instance) => {
39-
const newEntry = this.transformEventToNotificaton(changeInstance, 'Instance changed', 'link');
40-
this.applyUpdate(newEntry);
41-
});
27+
this.dataSource =
28+
new InfoCenterDataSource(this.storeService, this.socketService, this.paginator, this.sort, this.compType, this.instanceId);
4229
}
4330

44-
private transformEventToNotificaton(instance: Instance, notifName: string, type: string): InfoCenterItem {
45-
const datePipe = new DatePipe('en-US');
46-
const actualDate = datePipe.transform(Date.now(), 'dd/MM/yyyy hh:mm:ss:SSS');
47-
return {instanceId: instance.id, type: type,
48-
notifName: notifName, dateTime: actualDate, details: instance.name};
49-
}
50-
51-
private applyUpdate(newEntry: InfoCenterItem) {
52-
this.dataSource.add(newEntry);
53-
this.changeDetectorRefs.detectChanges();
54-
this.paginator.lastPage();
55-
this.paginator.firstPage();
56-
this.paginator._changePageSize(10);
57-
this.paginator._changePageSize(5);
58-
}
5931
}

0 commit comments

Comments
 (0)