GUI2 add in support for Preferences Service
Change-Id: Icdf2165d9f638aeff1b110a64777b93295935ed2
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.html b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.html
index 6fe0cb1..4b66bd6 100644
--- a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.html
+++ b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.html
@@ -18,13 +18,26 @@
-->
<onos-flash id="topoMsgFlash" message="{{ flashMsg }}" (closed)="flashMsg = ''"></onos-flash>
+<onos-quickhelp id="topoQuickHelp"></onos-quickhelp>
<!-- Template explanation - Add in the Panel components for the Topology view
These are referenced inside the typescript by @ViewChild and their label
-->
-<onos-instance #instance [divTopPx]="80" (mastershipEvent)="force.onosInstMastership = $event"></onos-instance>
-<onos-summary #summary></onos-summary>
-<onos-toolbar #toolbar (buttonEvent)="toolbarButtonClicked($event)"></onos-toolbar>
-<onos-details #details></onos-details>
+<onos-instance #instance [divTopPx]="80"
+ (mastershipEvent)="force.onosInstMastership = $event"
+ [on]="prefsState.insts">
+</onos-instance>
+<onos-summary #summary [on]="prefsState.summary"></onos-summary>
+<onos-toolbar #toolbar
+ (buttonEvent)="toolbarButtonClicked($event)"
+ [on]="prefsState.toolbar"
+ [backgroundVisible]="prefsState.bg"
+ [detailsVisible]="prefsState.detail"
+ [hostsVisible]="prefsState.hosts"
+ [instancesVisible]="prefsState.insts"
+ [portsVisible]="prefsState.porthl"
+ [summaryVisible]="prefsState.summary">
+</onos-toolbar>
+<onos-details #details [on]="prefsState.detail"></onos-details>
<div id="ov-topo2">
<!-- Template explanation -
@@ -49,8 +62,17 @@
onos-nodeviceconnected />
<svg:g id="topo-zoomlayer" onosZoomableOf [zoomableOf]="svgZoom">
<svg:desc>A logical layer that allows the main SVG canvas to be zoomed and panned</svg:desc>
- <svg:g *ngIf="showBackground" onos-backgroundsvg/>
- <svg:g #force onos-forcesvg (selectedNodeEvent)="nodeSelected($event)"/>
+ <svg:g *ngIf="prefsState.bg" onos-backgroundsvg>
+ <svg:desc>The Background SVG component - contains maps</svg:desc>
+ </svg:g>
+ <svg:g #force onos-forcesvg
+ [deviceLabelToggle]="prefsState.dlbls"
+ [hostLabelToggle]="prefsState.hlbls"
+ [showHosts]="prefsState.hosts"
+ [highlightPorts]="prefsState.porthl"
+ (selectedNodeEvent)="nodeSelected($event)">
+ <svg:desc>The Force SVG component - contains all the devices, hosts and links</svg:desc>
+ </svg:g>
</svg:g>
</svg:svg>
</div>
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.spec.ts b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.spec.ts
index 23fb257..446d41c 100644
--- a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.spec.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.spec.ts
@@ -1,5 +1,5 @@
/*
- * Copyright 2018-present Open Networking Foundation
+ * Copyright 2019-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -31,12 +31,20 @@
import {
FlashComponent,
+ QuickhelpComponent,
FnService,
LogService,
- IconService, IconComponent
+ IconService, IconComponent, PrefsService, KeysService, LionService
} from 'gui2-fw-lib';
import {ZoomableDirective} from '../layer/zoomable.directive';
import {RouterTestingModule} from '@angular/router/testing';
+import {TrafficService} from '../traffic.service';
+import {ForceSvgComponent} from '../layer/forcesvg/forcesvg.component';
+import {
+ DeviceNodeSvgComponent, HostNodeSvgComponent,
+ LinkSvgComponent, SubRegionNodeSvgComponent
+} from '../layer/forcesvg/visuals';
+import {DraggableDirective} from '../layer/forcesvg/draggable/draggable.directive';
class MockActivatedRoute extends ActivatedRoute {
@@ -78,6 +86,37 @@
loadIconDef() { }
}
+class MockKeysService {
+ quickHelpShown: boolean = true;
+
+ keyBindings(x) {
+ return {};
+ }
+
+ gestureNotes() {
+ return {};
+ }
+}
+
+class MockTrafficService {}
+
+class MockPrefsService {
+ listeners: ((data) => void)[] = [];
+
+ getPrefs() {
+ return { 'topo2_prefs': ''};
+ }
+
+ addListener(listener: (data) => void): void {
+ this.listeners.push(listener);
+ }
+
+ removeListener(listener: (data) => void) {
+ this.listeners = this.listeners.filter((obj) => obj !== listener);
+ }
+
+}
+
/**
* ONOS GUI -- Topology View -- Unit Tests
*/
@@ -89,6 +128,16 @@
let component: TopologyComponent;
let fixture: ComponentFixture<TopologyComponent>;
+ const bundleObj = {
+ 'core.fw.QuickHelp': {
+ test: 'test1',
+ tt_help: 'Help!'
+ }
+ };
+ const mockLion = (key) => {
+ return bundleObj[key] || '%' + key + '%';
+ };
+
beforeEach(async(() => {
const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
ar = new MockActivatedRoute({ 'debug': 'txrx' });
@@ -116,7 +165,15 @@
DetailsComponent,
FlashComponent,
ZoomableDirective,
- IconComponent
+ IconComponent,
+ QuickhelpComponent,
+ ForceSvgComponent,
+ LinkSvgComponent,
+ DeviceNodeSvgComponent,
+ HostNodeSvgComponent,
+ DraggableDirective,
+ ZoomableDirective,
+ SubRegionNodeSvgComponent
],
providers: [
{ provide: FnService, useValue: fs },
@@ -124,7 +181,18 @@
{ provide: 'Window', useValue: windowMock },
{ provide: HttpClient, useClass: MockHttpClient },
{ provide: TopologyService, useClass: MockTopologyService },
+ { provide: TrafficService, useClass: MockTrafficService },
{ provide: IconService, useClass: MockIconService },
+ { provide: PrefsService, useClass: MockPrefsService },
+ { provide: KeysService, useClass: MockKeysService },
+ { provide: LionService, useFactory: (() => {
+ return {
+ bundle: ((bundleId) => mockLion),
+ ubercache: new Array(),
+ loadCbs: new Map<string, () => void>([])
+ };
+ })
+ },
]
}).compileComponents();
logServiceSpy = TestBed.get(LogService);
diff --git a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts
index 3e1e1a6..ff8a050 100644
--- a/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts
+++ b/web/gui2/src/main/webapp/app/view/topology/topology/topology.component.ts
@@ -31,7 +31,6 @@
ZoomService
} from 'gui2-fw-lib';
import {InstanceComponent} from '../panel/instance/instance.component';
-import {SummaryComponent} from '../panel/summary/summary.component';
import {DetailsComponent} from '../panel/details/details.component';
import {BackgroundSvgComponent} from '../layer/backgroundsvg/backgroundsvg.component';
import {ForceSvgComponent} from '../layer/forcesvg/forcesvg.component';
@@ -41,10 +40,47 @@
LabelToggle,
UiElement
} from '../layer/forcesvg/models';
-import {ToolbarComponent} from '../panel/toolbar/toolbar.component';
+import {
+ INSTANCE_TOGGLE, SUMMARY_TOGGLE, DETAILS_TOGGLE,
+ HOSTS_TOGGLE, OFFLINE_TOGGLE, PORTS_TOGGLE,
+ BKGRND_TOGGLE, CYCLELABELS_BTN, CYCLEHOSTLABEL_BTN,
+ RESETZOOM_BTN, EQMASTER_BTN,
+ CANCEL_TRAFFIC, ALL_TRAFFIC, QUICKHELP_BTN
+} from '../panel/toolbar/toolbar.component';
import {TrafficService} from '../traffic.service';
import {ZoomableDirective} from '../layer/zoomable.directive';
+const TOPO2_PREFS = 'topo2_prefs';
+const PREF_BG = 'bg';
+const PREF_DETAIL = 'detail';
+const PREF_DLBLS = 'dlbls';
+const PREF_HLBLS = 'hlbls';
+const PREF_HOSTS = 'hosts';
+const PREF_INSTS = 'insts';
+const PREF_OFFDEV = 'offdev';
+const PREF_PORTHL = 'porthl';
+const PREF_SUMMARY = 'summary';
+const PREF_TOOLBAR = 'toolbar';
+
+/**
+ * model of the topo2_prefs object - this is a subset of the overall Prefs returned
+ * by the server
+ */
+export interface Topo2Prefs {
+ bg: number;
+ detail: number;
+ dlbls: number;
+ hlbls: number;
+ hosts: number;
+ insts: number;
+ offdev: number;
+ porthl: number;
+ spr: number;
+ ovid: string;
+ summary: number;
+ toolbar: number;
+}
+
/**
* ONOS GUI Topology View
*
@@ -77,17 +113,27 @@
export class TopologyComponent implements OnInit, OnDestroy {
// These are references to the components inserted in the template
@ViewChild(InstanceComponent) instance: InstanceComponent;
- @ViewChild(SummaryComponent) summary: SummaryComponent;
@ViewChild(DetailsComponent) details: DetailsComponent;
- @ViewChild(ToolbarComponent) toolbar: ToolbarComponent;
@ViewChild(BackgroundSvgComponent) background: BackgroundSvgComponent;
@ViewChild(ForceSvgComponent) force: ForceSvgComponent;
@ViewChild(ZoomableDirective) zoomDirective: ZoomableDirective;
flashMsg: string = '';
- prefsState = {};
- hostLabelIdx: number = 1;
- showBackground: boolean = false;
+ // These are used as defaults if nothing is set on the server
+ prefsState: Topo2Prefs = <Topo2Prefs>{
+ bg: 0,
+ detail: 1,
+ dlbls: 0,
+ hlbls: 2,
+ hosts: 0,
+ insts: 1,
+ offdev: 1,
+ ovid: 'traffic', // default to traffic overlay
+ porthl: 1,
+ spr: 0,
+ summary: 1,
+ toolbar: 0,
+ };
lionFn; // Function
constructor(
@@ -97,7 +143,6 @@
protected sus: SvgUtilService,
protected ps: PrefsService,
protected wss: WebSocketService,
- protected zs: ZoomService,
protected ts: TopologyService,
protected trs: TrafficService,
protected is: IconService,
@@ -169,15 +214,31 @@
// The handling of the WebSocket call is delegated out to the Topology
// Service just to compartmentalize things a bit
this.ts.init(this.instance, this.background, this.force);
+
+ this.ps.addListener((data) => this.prefsUpdateHandler(data));
+ this.prefsState = this.ps.getPrefs(TOPO2_PREFS, this.prefsState);
this.log.debug('Topology component initialized');
}
/**
+ * Callback function that's called whenever new Prefs are received from WebSocket
+ *
+ * Note: At present the backend server does not filter updated by logged in user,
+ * so you might get updates pertaining to a different user
+ */
+ prefsUpdateHandler(data: any): void {
+ // Extract the TOPO2 prefs from it
+ this.prefsState = data[TOPO2_PREFS];
+ this.log.debug('Updated topo2 prefs', this.prefsState);
+ }
+
+ /**
* When this component is being stopped, disconnect the TopologyService from
* the WebSocket
*/
ngOnDestroy() {
this.ts.destroy();
+ this.ps.removeListener((data) => this.prefsUpdateHandler(data));
this.log.debug('Topology component destroyed');
}
@@ -188,45 +249,48 @@
*/
toolbarButtonClicked(name: string) {
switch (name) {
- case 'instance-tog':
+ case INSTANCE_TOGGLE:
this.toggleInstancePanel();
break;
- case 'summary-tog':
+ case SUMMARY_TOGGLE:
this.toggleSummary();
break;
- case 'details-tog':
+ case DETAILS_TOGGLE:
this.toggleDetails();
break;
- case 'hosts-tog':
+ case HOSTS_TOGGLE:
this.toggleHosts();
break;
- case 'offline-tog':
+ case OFFLINE_TOGGLE:
this.toggleOfflineDevices();
break;
- case 'ports-tog':
+ case PORTS_TOGGLE:
this.togglePorts();
break;
- case 'bkgrnd-tog':
+ case BKGRND_TOGGLE:
this.toggleBackground();
break;
- case 'cycleLabels-btn':
+ case CYCLELABELS_BTN:
this.cycleDeviceLabels();
break;
- case 'cycleHostLabel-btn':
+ case CYCLEHOSTLABEL_BTN:
this.cycleHostLabels();
break;
- case 'resetZoom-btn':
+ case RESETZOOM_BTN:
this.resetZoom();
break;
- case 'eqMaster-btn':
+ case EQMASTER_BTN:
this.equalizeMasters();
break;
- case 'cancel-traffic':
+ case CANCEL_TRAFFIC:
this.cancelTraffic();
break;
- case 'all-traffic':
+ case ALL_TRAFFIC:
this.monitorAllTraffic();
break;
+ case QUICKHELP_BTN:
+ this.ks.quickHelpShown = true;
+ break;
default:
this.log.warn('Unhandled Toolbar action', name);
}
@@ -262,7 +326,7 @@
this.sus.cat7().testCard(d3.select('svg#topo2'));
},
- esc: this.handleEscape,
+ esc: [() => {this.handleEscape(); }, 'Cancel commands'],
// TODO update after adding in Background Service
// topology overlay selections
@@ -306,6 +370,7 @@
// TODO: Cancel Active overlay
// TODO: Reinstate with components
} else {
+ this.nodeSelected(undefined);
this.log.debug('Handling escape');
// } else if (t2rs.deselectAllNodes()) {
// // else if we have node selections, deselect them all
@@ -322,64 +387,111 @@
}
}
-
-
- updatePrefsState(what, b) {
- this.prefsState[what] = b ? 1 : 0;
- this.ps.setPrefs('topo2_prefs', this.prefsState);
+ /**
+ * Updates the cache of preferences locally and onwards to the PrefsService
+ * @param what The attribute of the local topo2-prefs cache to update
+ * @param b the value to update it with
+ */
+ updatePrefsState(what: string, b: number) {
+ this.prefsState[what] = b;
+ this.ps.setPrefs(TOPO2_PREFS, this.prefsState);
}
+ /**
+ * When the button is clicked on the toolbar or the L key is pressed
+ * 1) cycle through options
+ * 2) flash up a message
+ * 3a) Update the local prefs cache
+ * 3b) And passes on to the global prefs service which sends back to the server
+ * 3c) It also has a knock on effect of passing it on to ForceSvgComponent
+ * because prefsState.dlbls is given as an input to it
+ * 3d) This will in turn pass it down to the DeviceSvgComponent which
+ * displays the label
+ */
protected cycleDeviceLabels() {
- const old: LabelToggle = this.force.deviceLabelToggle;
+ const old: LabelToggle = this.prefsState.dlbls;
const next = LabelToggle.next(old);
- this.force.ngOnChanges({'deviceLabelToggle':
- new SimpleChange(old, next, false)});
this.flashMsg = this.lionFn(TopologyComponent.deviceLabelFlashMessage(next));
+ this.updatePrefsState(PREF_DLBLS, next);
this.log.debug('Cycling device labels', old, next);
}
protected cycleHostLabels() {
- const old: HostLabelToggle = this.force.hostLabelToggle;
+ const old: HostLabelToggle = this.prefsState.hlbls;
const next = HostLabelToggle.next(old);
- this.force.ngOnChanges({'hostLabelToggle':
- new SimpleChange(old, next, false)});
this.flashMsg = this.lionFn(TopologyComponent.hostLabelFlashMessage(next));
+ this.updatePrefsState(PREF_HLBLS, next);
this.log.debug('Cycling host labels', old, next);
}
+ /**
+ * When the button is clicked on the toolbar or the B key is pressed
+ * 1) Find the inverse of the current state (held as 1 or 0)
+ * 2) Flash up a message on screen
+ * 3b) And passes on to the global prefs service which sends back to the server
+ * 3c) It also has a knock on effect of passing it on to ToolbarComponent
+ * because prefsState.bg is given as an input to it
+ * @param token
+ */
protected toggleBackground(token?: KeysToken) {
- this.showBackground = !this.showBackground;
- this.flashMsg = this.lionFn(this.showBackground ? 'show' : 'hide') +
+ const bg: boolean = !Boolean(this.prefsState.bg);
+ this.flashMsg = this.lionFn(bg ? 'show' : 'hide') +
' ' + this.lionFn('fl_background_map');
- this.toolbar.backgroundVisible = this.showBackground;
- this.log.debug('Toggling background', token);
+ this.updatePrefsState(PREF_BG, bg ? 1 : 0);
+ this.log.debug('Toggling background', token, bg ? 'shown' : 'hidden');
}
protected toggleDetails(token?: KeysToken) {
- if (this.details.selectedNode) {
- const on: boolean = this.details.togglePanel(() => {
- });
- this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
- ' ' + this.lionFn('fl_panel_details');
- this.toolbar.detailsVisible = on;
-
- this.log.debug('Toggling details', token);
- }
+ const on: boolean = !Boolean(this.prefsState.detail);
+ this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
+ ' ' + this.lionFn('fl_panel_details');
+ this.updatePrefsState(PREF_DETAIL, on ? 1 : 0);
+ this.log.debug('Toggling details', token);
}
protected toggleInstancePanel(token?: KeysToken) {
- const on: boolean = this.instance.togglePanel(() => {});
+ const on: boolean = !Boolean(this.prefsState.insts);
this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
' ' + this.lionFn('fl_panel_instances');
- this.toolbar.instancesVisible = on;
+ this.updatePrefsState(PREF_INSTS, on ? 1 : 0);
this.log.debug('Toggling instances', token, on);
}
protected toggleSummary() {
- const on: boolean = this.summary.togglePanel(() => {});
+ const on: boolean = !Boolean(this.prefsState.summary);
this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
' ' + this.lionFn('fl_panel_summary');
- this.toolbar.summaryVisible = on;
+ this.updatePrefsState(PREF_SUMMARY, on ? 1 : 0);
+ }
+
+ protected togglePorts(token?: KeysToken) {
+ const current: boolean = !Boolean(this.prefsState.porthl);
+ this.flashMsg = this.lionFn(current ? 'enable' : 'disable') +
+ ' ' + this.lionFn('fl_port_highlighting');
+ this.updatePrefsState(PREF_PORTHL, current ? 1 : 0);
+ this.log.debug(current ? 'Enable' : 'Disable', 'port highlighting');
+ }
+
+ protected toggleToolbar() {
+ const on: boolean = !Boolean(this.prefsState.toolbar);
+ this.updatePrefsState(PREF_TOOLBAR, on ? 1 : 0);
+ this.log.debug('toggling toolbar', on ? 'shown' : 'hidden');
+ }
+
+ protected toggleHosts() {
+ const current: boolean = !Boolean(this.prefsState.hosts);
+ this.flashMsg = this.lionFn('hosts') + ' ' +
+ this.lionFn(this.force.showHosts ? 'visible' : 'hidden');
+ this.updatePrefsState(PREF_HOSTS, current ? 1 : 0);
+ this.log.debug('toggling hosts: ', this.prefsState.hosts ? 'Show' : 'Hide');
+ }
+
+ protected toggleOfflineDevices() {
+ const on: boolean = !Boolean(this.prefsState.offdev);
+ this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
+ ' ' + this.lionFn('fl_offline_devices');
+ this.updatePrefsState(PREF_OFFDEV, on ? 1 : 0);
+ this.log.debug('toggling offline devices', this.prefsState.offdev);
}
protected resetZoom() {
@@ -387,16 +499,6 @@
this.flashMsg = this.lionFn('fl_pan_zoom_reset');
}
- protected togglePorts(token?: KeysToken) {
- const old: boolean = this.force.highlightPorts;
- const current: boolean = !this.force.highlightPorts;
- this.force.ngOnChanges({'highlightPorts': new SimpleChange(old, current, false)});
- this.flashMsg = this.lionFn(current ? 'enable' : 'disable') +
- ' ' + this.lionFn('fl_port_highlighting');
- this.toolbar.portsVisible = current;
- this.log.debug(current ? 'Enable' : 'Disable', 'port highlighting');
- }
-
protected equalizeMasters() {
this.wss.sendEvent('equalizeMasters', null);
this.flashMsg = this.lionFn('fl_eq_masters');
@@ -414,29 +516,6 @@
this.log.debug('unpinning node');
}
- protected toggleToolbar() {
- this.log.debug('toggling toolbar');
- this.toolbar.on = !this.toolbar.on;
- }
-
- protected toggleHosts() {
- const old: boolean = this.force.showHosts;
- const current = !this.force.showHosts;
- this.force.ngOnChanges({'showHosts': new SimpleChange(old, current, false)});
- this.flashMsg = this.lionFn('hosts') + ' ' +
- this.lionFn(this.force.showHosts ? 'visible' : 'hidden');
- this.toolbar.hostsVisible = current;
- this.log.debug('toggling hosts: ', this.force.showHosts ? 'Show' : 'Hide');
- }
-
- protected toggleOfflineDevices() {
- // TODO: Implement toggle offline visibility
- const on: boolean = true;
- this.flashMsg = this.lionFn(on ? 'show' : 'hide') +
- ' ' + this.lionFn('fl_offline_devices');
- this.log.debug('toggling offline devices');
- }
-
/**
* Check to see if this is needed anymore
* @param what
@@ -474,7 +553,6 @@
nodeSelected(nodeOrLink: UiElement) {
this.details.ngOnChanges({'selectedNode':
new SimpleChange(undefined, nodeOrLink, true)});
- this.details.on = Boolean(nodeOrLink);
}
/**