mirror of
https://github.com/opennetworkinglab/onos.git
synced 2025-12-15 22:31:50 +01:00
Implemented table building functions
Change-Id: Ie4003080b13725561df22de41ec85f8c3f31c794
This commit is contained in:
parent
667538d4ea
commit
2bd11b79bf
@ -88,14 +88,17 @@ There is a fair bit of refactoring has to take place. An important thing to unde
|
||||
is that DOM manipulations from inside JavaScript code is not the Angular 6
|
||||
way of doing things - there was a lot of this in the old ONOS GUI, using d3.append(..)
|
||||
and so on.
|
||||
The Angular 6 way of doing things is to defined DOM objects (elements) in the
|
||||
The Angular 6 way of doing things is to define DOM objects (elements) in the
|
||||
html template of a component, and use the Component Java Script code as a base
|
||||
for logic that can influence the display of these objects in the template.
|
||||
What this means is that what were previously defined as services (e.g. VeilService or
|
||||
LoadingService) should now become Components in Angular 6 (e.g. VeilComponent or
|
||||
LoadingComponent).
|
||||
|
||||
###How do I know whether a Service should be made a Component in this new GUI?
|
||||
Similarly a directive might be trying to do DOM manipulation and have a CSS - this
|
||||
should be made in to a component instead (see IconComponent)
|
||||
|
||||
###How do I know whether a Service or Directive should be made a Component in this new GUI?
|
||||
The general rule to follow is _"if a service in the old GUI has an associated CSS
|
||||
file or two then is should be a component in the new GUI"_.
|
||||
|
||||
@ -129,6 +132,15 @@ Any component that needs to use WSS for data should inject the WSS service __AND
|
||||
needs to include the components in its template by adding <onos-loading> and
|
||||
<onos-veil>.
|
||||
|
||||
### Consider if a service is really needs to be a service that runs all the time
|
||||
Or does it just support a few functions. See the TableBase class. This now
|
||||
replaces the old TableBuilderService - that was just on function that
|
||||
manipulated the scope of a view component. Instead view components now
|
||||
extend this class.
|
||||
|
||||
Also sometimes directive are always used together e.g. icon directive and tooltip
|
||||
directive and they can be merged in to one
|
||||
|
||||
## fw/remote/wsock.service
|
||||
Taking for a really simple example the fw/remote/WSockService, this was originally defined in
|
||||
the __app/fw/remote/wsock.js__ file and is now redefined in
|
||||
@ -222,22 +234,49 @@ There are several things worth noting here:
|
||||
* (d3 object).append(..).attr values should be listed individually (see icon.service for example)
|
||||
* Please try do avoid d3 DOM manipulations in ONOS GUI 2, as this is not the Angular 6 way of
|
||||
doing things
|
||||
* $interval should be replaced by
|
||||
task = setInterval(() => functionname_or_body, speed);
|
||||
* To cancel the timer clearInterval(task)
|
||||
|
||||
|
||||
# Progress so far - 24 May 2018
|
||||
# Progress so far - 18 Jun 2018
|
||||
The following services are partially migrated:
|
||||
* fw/util/FnService - some essential components of this have been migrated - still lots to do
|
||||
* fw/util/FnService - full migrated with Unit tests
|
||||
* fw/svg/GlyphDataService - mostly migrated. Values are stored in maps as constants
|
||||
* fw/svg/GlyphService - partly implemented - enough to support the icon service
|
||||
* fw/svg/IconService - mostly implemented - enough to support the IconDirective
|
||||
* fw/svg/IconDirective - mostly implemented - although want to replace this with
|
||||
the IconComponent
|
||||
* fw/svg/icon/IconComponent - replacement for the IconDirective - decided to make
|
||||
it a component because it has CSS files which are scoped to just that component
|
||||
* fw/layer/LoadingService - mostly implemented - I'm leaving this as a Service,
|
||||
although maybe it should become a component - its CSS is has to be loaded
|
||||
globally in index.html
|
||||
It also incorporates the old fw/widget/tooltip.js which was a directive - combined
|
||||
tooltip in to icon because the 2 are always used together in tabular views
|
||||
* fw/layer/LoadingService - mostly implemented - this should become a component
|
||||
- its CSS is has to be loaded globally in index.html
|
||||
* fw/layer/flash/FlashComponent - implemented as a Component instead of the old Flash Service
|
||||
because it has a CSS file. Replaced all of the D3 Dom manipulations with Template code
|
||||
in the Angular 6 style of doing things
|
||||
* fw/layer/veil/VeilComponent - changed to a component - fully implemented
|
||||
* fw/remote/urlfn.service - fully implemented with Unit tests
|
||||
* fw/remote/WebSocketService - fully implemented with Unit tests
|
||||
* fw/widget/TableBase - previously the TableBuilderService this has now been changed
|
||||
to a plain interface and class - any table views should extend this
|
||||
|
||||
|
||||
# Devices View
|
||||
This is now a Component, whose class extends the TableBase - this is where it gets
|
||||
most of its functionality. As before all data comes from the WebSocket.
|
||||
There is still a bit of work to go on this - scrolling of long lists, device details
|
||||
panel etc
|
||||
|
||||
The major change in the template (html) is that there used to be 2 tables and these
|
||||
are now brought together in to a header and body. This simplifies trying to keep
|
||||
the widths of both in sync.
|
||||
|
||||
For CSS the old device view CSS is included and a link is made across to the
|
||||
common table CSS
|
||||
|
||||
#Apps View
|
||||
This is a Component too, again extending TableBase. Apps view has much more functionality
|
||||
though because it has controls for upload and download of applications.
|
||||
|
||||
|
||||
|
||||
|
||||
@ -62,7 +62,7 @@ genrule(
|
||||
+ 'npm5 install -g @angular/cli@6.0.0 2>&1;'
|
||||
+ 'npm5 install 2>&1;'
|
||||
+ 'ng -v;'
|
||||
+ 'ng build --preserve-symlinks --base-href /onos/ui2/dist/ --deploy-url /onos/ui2/dist/ --output-path="$OUT" 2>&1',
|
||||
+ 'ng build --extract-css --preserve-symlinks --base-href /onos/ui2/dist/ --deploy-url /onos/ui2/dist/ --output-path="$OUT" 2>&1',
|
||||
out = 'dist',
|
||||
visibility = [ 'PUBLIC' ],
|
||||
)
|
||||
|
||||
@ -9,14 +9,23 @@ To use this new GUI you simply have to start the GUI in a running ONOS at the __
|
||||
```
|
||||
feature:install onos-gui2
|
||||
```
|
||||
and the gui will be accessible at [http://localhost:8181/onos/ui2/dist/](http://localhost:8181/onos/ui2/dist/)
|
||||
and the gui will be accessible at [http://localhost:8181/onos/ui2](http://localhost:8181/onos/ui2)
|
||||
|
||||
# Development
|
||||
The project relies on [Angular CLI](https://github.com/angular/angular-cli) v6 to simplify development of the browser side code.
|
||||
There are 2 ways to go about development -
|
||||
1. rebuild the code and rerun through BUCK OR (much like can be done with any ordinary ONOS app)
|
||||
2. use Angular 6 CLI (ng command) to rebuild on the fly (must faster for development)
|
||||
|
||||
This allows you to develop the Angular 6 Type Script code independent of ONOS in a separate container. At the current moment (May '18) the
|
||||
implementation of WebSockets and REST calls is not done, so there is __no__ requirement to run ONOS in the background.
|
||||
This will change in the coming weeks.
|
||||
For 1) if you change the code you can redeploy the application with (requies you to be in ~/onos directory):
|
||||
```
|
||||
onos-buck build //web/gui2:onos-web-gui2-oar --show-output|grep /app.oar | cut -d\ -f2 | xargs onos-app localhost reinstall!
|
||||
```
|
||||
|
||||
For 2) it's well worth becoming familiar with Angular CLI.
|
||||
The project is created with [Angular CLI](https://github.com/angular/angular-cli) v6 to simplify development of the browser side code.
|
||||
|
||||
This allows you to develop the Angular 6 Type Script code independent of ONOS in a separate container.
|
||||
Since WebSockets have been implemented (Jun 18) there is a requirement to run ONOS in the background.
|
||||
|
||||
There is no need to install node, npm or ng again on your system, and indeed if they are already installed, it's best
|
||||
to use the versions of these that's used by BUCK. To do this add to the __start__ of your PATH environment variable.
|
||||
@ -64,7 +73,8 @@ To run it manually in Angular CLI run `ng build`
|
||||
## Running unit tests
|
||||
This is automatically done when using "onos-buck test" - see the web/gui2/BUCK file for more details.
|
||||
|
||||
To run it manually in Angular CLI run `ng test --watch=true` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
To run it manually in Angular CLI run `ng test --watch` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
Running it directly like this will test with both Firefox and Chrome. To use only one use the __--browsers__ argument
|
||||
|
||||
## Running checkstyle
|
||||
This is automatically done when using "onos-buck test" - see the web/gui2/BUCK file for more details.
|
||||
|
||||
@ -8,6 +8,7 @@ module.exports = function (config) {
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-firefox-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage-istanbul-reporter'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
@ -27,7 +28,7 @@ module.exports = function (config) {
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
browsers: ['Chrome', 'Firefox'],
|
||||
singleRun: false
|
||||
});
|
||||
};
|
||||
|
||||
6
web/gui2/package-lock.json
generated
6
web/gui2/package-lock.json
generated
@ -8446,6 +8446,12 @@
|
||||
"minimatch": "3.0.4"
|
||||
}
|
||||
},
|
||||
"karma-firefox-launcher": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/karma-firefox-launcher/-/karma-firefox-launcher-1.1.0.tgz",
|
||||
"integrity": "sha512-LbZ5/XlIXLeQ3cqnCbYLn+rOVhuMIK9aZwlP6eOLGzWdo1UVp7t6CN3DP4SafiRLjexKwHeKHDm0c38Mtd3VxA==",
|
||||
"dev": true
|
||||
},
|
||||
"karma-jasmine": {
|
||||
"version": "1.1.1",
|
||||
"resolved": "https://registry.npmjs.org/karma-jasmine/-/karma-jasmine-1.1.1.tgz",
|
||||
|
||||
@ -28,10 +28,9 @@
|
||||
"zone.js": "^0.8.26"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/compiler-cli": "^6.0.0",
|
||||
"@angular-devkit/build-angular": "~0.6.0",
|
||||
"typescript": "~2.7.2",
|
||||
"@angular/cli": "~6.0.0",
|
||||
"@angular/compiler-cli": "^6.0.0",
|
||||
"@angular/language-service": "^6.0.0",
|
||||
"@types/jasmine": "~2.8.6",
|
||||
"@types/jasminewd2": "~2.0.3",
|
||||
@ -42,10 +41,12 @@
|
||||
"karma": "~1.7.1",
|
||||
"karma-chrome-launcher": "~2.2.0",
|
||||
"karma-coverage-istanbul-reporter": "~1.4.2",
|
||||
"karma-firefox-launcher": "^1.1.0",
|
||||
"karma-jasmine": "~1.1.1",
|
||||
"karma-jasmine-html-reporter": "^0.2.2",
|
||||
"protractor": "~5.3.0",
|
||||
"ts-node": "~5.0.1",
|
||||
"tslint": "~5.9.1"
|
||||
"tslint": "~5.9.1",
|
||||
"typescript": "~2.7.2"
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,9 +17,7 @@ import { Injectable } from '@angular/core';
|
||||
import { environment } from '../environments/environment';
|
||||
import { Logger } from './log.service';
|
||||
|
||||
export let isDebugMode = environment.isDebugMode;
|
||||
|
||||
const noop = (): any => undefined;
|
||||
export let isDebugMode: boolean = !environment.production;
|
||||
|
||||
/**
|
||||
* ONOS GUI -- LogService
|
||||
@ -27,12 +25,13 @@ const noop = (): any => undefined;
|
||||
*/
|
||||
@Injectable()
|
||||
export class ConsoleLoggerService implements Logger {
|
||||
private noop: () => void;
|
||||
|
||||
get debug() {
|
||||
if (isDebugMode) {
|
||||
return console.debug.bind(console);
|
||||
} else {
|
||||
return noop;
|
||||
return this.noop;
|
||||
}
|
||||
}
|
||||
|
||||
@ -40,7 +39,7 @@ export class ConsoleLoggerService implements Logger {
|
||||
if (isDebugMode) {
|
||||
return console.info.bind(console);
|
||||
} else {
|
||||
return noop;
|
||||
return this.noop;
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,7 +52,7 @@ export class ConsoleLoggerService implements Logger {
|
||||
}
|
||||
|
||||
invokeConsoleMethod(type: string, args?: any): void {
|
||||
const logFn: Function = (console)[type] || console.log || noop;
|
||||
const logFn: Function = (console)[type] || console.log || this.noop;
|
||||
logFn.apply(console, [args]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -38,4 +38,7 @@ export class DialogService {
|
||||
this.log.debug('DialogService constructed');
|
||||
}
|
||||
|
||||
createDiv() {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -33,7 +33,9 @@ const waitDelay = 500;
|
||||
*
|
||||
* Provides a mechanism to start/stop the loading animation, center screen.
|
||||
*/
|
||||
@Injectable()
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class LoadingService {
|
||||
images: any[] = [];
|
||||
idx = 0;
|
||||
@ -127,7 +129,7 @@ export class LoadingService {
|
||||
}
|
||||
|
||||
// return true if start() has been called but not stop()
|
||||
waiting() {
|
||||
waiting(): boolean {
|
||||
return !!this.wait;
|
||||
}
|
||||
|
||||
|
||||
@ -90,7 +90,6 @@ export class WebSocketService {
|
||||
* built-in handler for the 'boostrap' event
|
||||
*/
|
||||
private bootstrap(data: Bootstrap) {
|
||||
this.log.debug('bootstrap data', data);
|
||||
this.loggedInUser = data.user;
|
||||
|
||||
this.clusterNodes = data.clusterNodes;
|
||||
@ -332,6 +331,13 @@ export class WebSocketService {
|
||||
return this.url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the WebSocket to close - this should call the handleClose() method
|
||||
*/
|
||||
closeWebSocket() {
|
||||
this.ws.close();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Binds the message handlers to their message type (event type) as
|
||||
@ -369,12 +375,12 @@ export class WebSocketService {
|
||||
* Unbinds the specified message handlers.
|
||||
* Expected that the same map will be used, but we only care about keys
|
||||
*/
|
||||
unbindHandlers(handlerMap: Map<string, (data) => void>): void {
|
||||
if (this.noHandlersWarn(handlerMap, 'unbindHandlers')) {
|
||||
unbindHandlers(handlerIds: string[]): void {
|
||||
if ( handlerIds.length === 0 ) {
|
||||
this.log.warn('WSS.unbindHandlers(): no event handlers');
|
||||
return null;
|
||||
}
|
||||
|
||||
for (const [eventId, api] of handlerMap) {
|
||||
for (const eventId of handlerIds) {
|
||||
this.handlers.delete(eventId);
|
||||
}
|
||||
}
|
||||
@ -448,4 +454,7 @@ export class WebSocketService {
|
||||
this.lcd = ld;
|
||||
}
|
||||
|
||||
isConnected(): boolean {
|
||||
return this.wsUp;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,50 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Directive, ElementRef, Input, OnInit } from '@angular/core';
|
||||
import { IconService } from './icon.service';
|
||||
import { LogService } from '../../log.service';
|
||||
import * as d3 from 'd3';
|
||||
|
||||
/**
|
||||
* ONOS GUI -- SVG -- Icon Directive
|
||||
*
|
||||
* TODO: Deprecated - this directive may be removed altogether as it has been
|
||||
* rebuilt as IconComponent instead
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[onosIcon]'
|
||||
})
|
||||
export class IconDirective implements OnInit {
|
||||
@Input() iconId: string;
|
||||
@Input() iconSize = 20;
|
||||
|
||||
constructor(
|
||||
private el: ElementRef,
|
||||
private is: IconService,
|
||||
private log: LogService
|
||||
) {
|
||||
// Note: iconId is not available until initialization
|
||||
this.log.debug('IconDirective constructed');
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
const div = d3.select(this.el.nativeElement);
|
||||
div.selectAll('*').remove();
|
||||
this.is.loadEmbeddedIcon(div, this.iconId, this.iconSize);
|
||||
this.log.debug('IconDirective initialized:', this.iconId);
|
||||
}
|
||||
|
||||
}
|
||||
@ -13,9 +13,11 @@
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
<svg class="embeddedIcon" [attr.width]="iconSize" [attr.height]="iconSize" viewBox="0 0 50 50">
|
||||
<g class="icon" [ngClass]="iconId">
|
||||
<svg class="embeddedIcon" [attr.width]="iconSize" [attr.height]="iconSize" viewBox="0 0 50 50" (mouseover)="toolTipDisp = toolTip" (mouseout)="toolTipDisp = undefined">
|
||||
<g class="icon" [ngClass]="classes">
|
||||
<rect width="50" height="50" rx="5"></rect>
|
||||
<use width="50" height="50" class="glyph" [attr.href]="iconTag()"></use>
|
||||
</g>
|
||||
</svg>
|
||||
<!-- I'm fixing class as light as view encapsulation changes how the hirerarchy of css is handled -->
|
||||
<p id="tooltip" class="light" *ngIf="toolTip" [ngStyle]="{ 'display': toolTipDisp ? 'inline-block':'none' }">{{ toolTipDisp }}</p>
|
||||
|
||||
|
Before Width: | Height: | Size: 892 B After Width: | Height: | Size: 1.2 KiB |
@ -16,7 +16,6 @@
|
||||
import { Component, OnInit, Input } from '@angular/core';
|
||||
import { IconService, glyphMapping } from '../icon.service';
|
||||
import { LogService } from '../../../log.service';
|
||||
import * as d3 from 'd3';
|
||||
|
||||
/**
|
||||
* Icon Component
|
||||
@ -31,11 +30,20 @@ import * as d3 from 'd3';
|
||||
@Component({
|
||||
selector: 'onos-icon',
|
||||
templateUrl: './icon.component.html',
|
||||
styleUrls: ['./icon.component.css', './icon.theme.css', './glyph.css', './glyph-theme.css']
|
||||
styleUrls: [
|
||||
'./icon.component.css', './icon.theme.css',
|
||||
'./glyph.css', './glyph-theme.css',
|
||||
'./tooltip.css', './tooltip-theme.css'
|
||||
]
|
||||
})
|
||||
export class IconComponent implements OnInit {
|
||||
@Input() iconId: string;
|
||||
@Input() iconSize = 20;
|
||||
@Input() iconSize: number = 20;
|
||||
@Input() toolTip: string = undefined;
|
||||
@Input() classes: string = undefined;
|
||||
|
||||
// The displayed tooltip - undefined until mouse hovers over, then equals toolTip
|
||||
toolTipDisp: string = undefined;
|
||||
|
||||
constructor(
|
||||
private is: IconService,
|
||||
@ -47,7 +55,6 @@ export class IconComponent implements OnInit {
|
||||
|
||||
ngOnInit() {
|
||||
this.is.loadIconDef(this.iconId);
|
||||
this.log.debug('IconComponent initialized for ', this.iconId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -40,6 +40,46 @@
|
||||
fill: #3c3a3a;
|
||||
}
|
||||
|
||||
/* --- Control Buttons --- */
|
||||
|
||||
/* INACTIVE */
|
||||
svg.embeddedIcon g.icon use {
|
||||
fill: #e0dfd6;
|
||||
}
|
||||
/* note: no change for inactive buttons when hovered */
|
||||
|
||||
|
||||
/* ACTIVE */
|
||||
svg.embeddedIcon g.icon.active use {
|
||||
fill: #939598;
|
||||
}
|
||||
svg.embeddedIcon g.icon.active:hover use {
|
||||
fill: #ce5b58;
|
||||
}
|
||||
|
||||
/* CURRENT-VIEW */
|
||||
svg.embeddedIcon g.icon.current-view rect {
|
||||
fill: #518ecc;
|
||||
}
|
||||
svg.embeddedIcon g.icon.current-view use {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
/* REFRESH */
|
||||
svg.embeddedIcon g.icon.refresh use {
|
||||
fill: #cdeff2;
|
||||
}
|
||||
svg.embeddedIcon g.icon.refresh:hover use {
|
||||
fill: #ce5b58;
|
||||
}
|
||||
svg.embeddedIcon g.icon.refresh.active use {
|
||||
fill: #009fdb;
|
||||
}
|
||||
svg.embeddedIcon g.icon.refresh.active:hover use {
|
||||
fill: #ce5b58;
|
||||
}
|
||||
|
||||
|
||||
/* ========== DARK Theme ========== */
|
||||
|
||||
.dark div.close-btn svg.embeddedIcon g.icon .glyph {
|
||||
@ -61,11 +101,12 @@
|
||||
.dark table svg.embeddedIcon .icon .glyph {
|
||||
fill: #9999aa;
|
||||
}
|
||||
|
||||
/*
|
||||
svg.embeddedIcon g.icon .glyph {
|
||||
fill: #007dc4;
|
||||
}
|
||||
|
||||
svg.embeddedIcon:hover g.icon .glyph {
|
||||
fill: #20b2ff;
|
||||
}
|
||||
}
|
||||
*/
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2015-present Open Networking Foundation
|
||||
* Copyright 2016-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.
|
||||
@ -13,23 +13,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Directive } from '@angular/core';
|
||||
import { FnService } from '../util/fn.service';
|
||||
import { LogService } from '../../log.service';
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Widget -- Tooltip Directive
|
||||
/*
|
||||
ONOS GUI -- Tooltip Service (theme) -- CSS file
|
||||
*/
|
||||
@Directive({
|
||||
selector: '[onosTooltip]'
|
||||
})
|
||||
export class TooltipDirective {
|
||||
|
||||
constructor(
|
||||
private fs: FnService,
|
||||
private log: LogService
|
||||
) {
|
||||
this.log.debug('TooltipDirective constructed');
|
||||
}
|
||||
|
||||
.light#tooltip {
|
||||
background-color: #dbeffc;
|
||||
color: #3c3a3a;
|
||||
border-color: #c7c7c0;
|
||||
}
|
||||
|
||||
.dark#tooltip {
|
||||
background-color: #3a3a60;
|
||||
border-color: #6c6a6a;
|
||||
color: #c7c7c0;
|
||||
}
|
||||
@ -13,21 +13,18 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FnService } from '../util/fn.service';
|
||||
import { LogService } from '../../log.service';
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Widget -- Tooltip Service
|
||||
/*
|
||||
ONOS GUI -- Tooltip Service (layout) -- CSS file
|
||||
*/
|
||||
@Injectable()
|
||||
export class TooltipService {
|
||||
|
||||
constructor(
|
||||
private fs: FnService,
|
||||
private log: LogService,
|
||||
) {
|
||||
this.log.debug('TooltipService constructed');
|
||||
}
|
||||
|
||||
#tooltip {
|
||||
text-align: center;
|
||||
font-size: 80%;
|
||||
border: 1px solid;
|
||||
padding: 5px;
|
||||
position: absolute;
|
||||
z-index: 5000;
|
||||
display: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
@ -25,7 +25,6 @@ import { MapService } from './map.service';
|
||||
import { SpriteService } from './sprite.service';
|
||||
import { SpriteDataService } from './spritedata.service';
|
||||
import { SvgUtilService } from './svgutil.service';
|
||||
import { IconDirective } from './icon.directive';
|
||||
import { IconComponent } from './icon/icon.component';
|
||||
|
||||
/**
|
||||
@ -33,7 +32,6 @@ import { IconComponent } from './icon/icon.component';
|
||||
*/
|
||||
@NgModule({
|
||||
exports: [
|
||||
IconDirective,
|
||||
IconComponent
|
||||
],
|
||||
imports: [
|
||||
@ -41,7 +39,6 @@ import { IconComponent } from './icon/icon.component';
|
||||
UtilModule
|
||||
],
|
||||
declarations: [
|
||||
IconDirective,
|
||||
IconComponent
|
||||
],
|
||||
providers: [
|
||||
|
||||
@ -414,6 +414,35 @@ export class FnService {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if the two objects have all the same properties
|
||||
*/
|
||||
sameObjProps(obj1: Object, obj2: Object): boolean {
|
||||
for (const key in obj1) {
|
||||
if (obj1.hasOwnProperty(key)) {
|
||||
if (!(obj1[key] === obj2[key])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* returns true if the array contains the object
|
||||
* does NOT use strict object reference equality,
|
||||
* instead checks each property individually for equality
|
||||
*/
|
||||
containsObj(arr: any[], obj: Object): boolean {
|
||||
const len = arr.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (this.sameObjProps(arr[i], obj)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the given string with the first character capitalized.
|
||||
*/
|
||||
|
||||
@ -34,7 +34,8 @@ interface Lion {
|
||||
})
|
||||
export class LionService {
|
||||
|
||||
ubercache: any[];
|
||||
ubercache: any[] = [];
|
||||
loadCb; // Function
|
||||
|
||||
/**
|
||||
* Handler for uberlion event from WSS
|
||||
@ -50,6 +51,10 @@ export class LionService {
|
||||
this.log.info(' :=> ', p);
|
||||
}
|
||||
}
|
||||
if (this.loadCb) {
|
||||
this.log.debug('Calling the load callback');
|
||||
this.loadCb();
|
||||
}
|
||||
|
||||
this.log.debug('LION service: uber-lion bundle received:', data);
|
||||
}
|
||||
@ -69,17 +74,15 @@ export class LionService {
|
||||
* returns a function that takes a string and returns a string
|
||||
*/
|
||||
bundle(bundleId: string): (string) => string {
|
||||
let bundle = this.ubercache[bundleId];
|
||||
let bundleObj = this.ubercache[bundleId];
|
||||
|
||||
if (!bundle) {
|
||||
if (!bundleObj) {
|
||||
this.log.warn('No lion bundle registered:', bundleId);
|
||||
bundle = {};
|
||||
bundleObj = {};
|
||||
}
|
||||
|
||||
return this.getKey;
|
||||
}
|
||||
|
||||
getKey(key: string): string {
|
||||
return this.bundle[key] || '%' + key + '%';
|
||||
return (key) => {
|
||||
return bundleObj[key] || '%' + key + '%';
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,7 +17,6 @@ import { Injectable } from '@angular/core';
|
||||
import { FnService } from '../util/fn.service';
|
||||
import { IconService } from '../svg/icon.service';
|
||||
import { LogService } from '../../log.service';
|
||||
import { TooltipService } from './tooltip.service';
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Widget -- Button Service
|
||||
@ -29,7 +28,6 @@ export class ButtonService {
|
||||
private is: IconService,
|
||||
private fs: FnService,
|
||||
private log: LogService,
|
||||
private tts: TooltipService
|
||||
) {
|
||||
this.log.debug('ButtonService constructed');
|
||||
}
|
||||
|
||||
152
web/gui2/src/main/webapp/app/fw/widget/table-theme.css
Normal file
152
web/gui2/src/main/webapp/app/fw/widget/table-theme.css
Normal file
@ -0,0 +1,152 @@
|
||||
/*
|
||||
* Copyright 2016-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* ------ for summary-list tables (theme) ------ */
|
||||
|
||||
.light div.summary-list, .table-header th {
|
||||
background-color: #e5e5e6;
|
||||
color: #3c3a3a;
|
||||
}
|
||||
|
||||
.light div.summary-list, td {
|
||||
color: #3c3a3a;
|
||||
}
|
||||
|
||||
.light div.summary-list, tr:nth-child(even) {
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
.light div.summary-list, tr:nth-child(odd) {
|
||||
background-color: #fbfbfb;
|
||||
}
|
||||
|
||||
.light div.summary-list, tr.selected {
|
||||
background-color: #dbeffc !important;
|
||||
}
|
||||
|
||||
|
||||
.light div.summary-list, tr.data-change {
|
||||
background-color: #FDFFDC;
|
||||
}
|
||||
|
||||
/* --- Control Buttons --- */
|
||||
|
||||
/* INACTIVE */
|
||||
.light .ctrl-btns div svg.embeddedIcon g.icon use {
|
||||
fill: #e0dfd6;
|
||||
}
|
||||
/* note: no change for inactive buttons when hovered */
|
||||
|
||||
|
||||
/* ACTIVE */
|
||||
.light .ctrl-btns div.active svg.embeddedIcon g.icon use {
|
||||
fill: #939598;
|
||||
}
|
||||
.light .ctrl-btns div.active:hover svg.embeddedIcon g.icon use {
|
||||
fill: #ce5b58;
|
||||
}
|
||||
|
||||
/* CURRENT-VIEW */
|
||||
.light .ctrl-btns div.current-view svg.embeddedIcon g.icon rect {
|
||||
fill: #518ecc;
|
||||
}
|
||||
.light .ctrl-btns div.current-view svg.embeddedIcon g.icon use {
|
||||
fill: white;
|
||||
}
|
||||
|
||||
/* REFRESH */
|
||||
.light .ctrl-btns div.refresh svg.embeddedIcon g.icon use {
|
||||
fill: #cdeff2;
|
||||
}
|
||||
.light .ctrl-btns div.refresh:hover svg.embeddedIcon g.icon use {
|
||||
fill: #ce5b58;
|
||||
}
|
||||
.light .ctrl-btns div.refresh.active svg.embeddedIcon g.icon use {
|
||||
fill: #009fdb;
|
||||
}
|
||||
.light .ctrl-btns div.refresh.active:hover svg.embeddedIcon g.icon use {
|
||||
fill: #ce5b58;
|
||||
}
|
||||
|
||||
|
||||
/* ========== DARK Theme ========== */
|
||||
|
||||
.dark div.summary-list .table-header td {
|
||||
background-color: #222222;
|
||||
color: #cccccc;
|
||||
}
|
||||
|
||||
.dark div.summary-list td {
|
||||
/* note: don't put background-color here */
|
||||
color: #cccccc;
|
||||
}
|
||||
.dark div.summary-list tr.no-data td {
|
||||
background-color: #333333;
|
||||
}
|
||||
|
||||
.dark div.summary-list tr:nth-child(even) {
|
||||
background-color: #333333;
|
||||
}
|
||||
.dark div.summary-list tr:nth-child(odd) {
|
||||
background-color: #3a3a3a;
|
||||
}
|
||||
|
||||
.dark div.summary-list tr.selected {
|
||||
background-color: #304860 !important;
|
||||
}
|
||||
|
||||
|
||||
.dark div.summary-list tr.data-change {
|
||||
background-color: #423708;
|
||||
}
|
||||
|
||||
/* --- Control Buttons --- */
|
||||
|
||||
/* INACTIVE */
|
||||
.dark .ctrl-btns div svg.embeddedIcon g.icon use {
|
||||
fill: #444444;
|
||||
}
|
||||
/* note: no change for inactive buttons when hovered */
|
||||
|
||||
|
||||
/* ACTIVE */
|
||||
.dark .ctrl-btns div.active svg.embeddedIcon g.icon use {
|
||||
fill: #939598;
|
||||
}
|
||||
.dark .ctrl-btns div.active:hover svg.embeddedIcon g.icon use {
|
||||
fill: #ce5b58;
|
||||
}
|
||||
|
||||
/* CURRENT-VIEW */
|
||||
.dark .ctrl-btns div.current-view svg.embeddedIcon g.icon rect {
|
||||
fill: #518ecc;
|
||||
}
|
||||
.dark .ctrl-btns div.current-view svg.embeddedIcon g.icon use {
|
||||
fill: #dddddd;
|
||||
}
|
||||
|
||||
/* REFRESH */
|
||||
.dark .ctrl-btns div.refresh svg.embeddedIcon g.icon use {
|
||||
fill: #364144;
|
||||
}
|
||||
.dark .ctrl-btns div.refresh:hover svg.embeddedIcon g.icon use {
|
||||
fill: #ce5b58;
|
||||
}
|
||||
.dark .ctrl-btns div.refresh.active svg.embeddedIcon g.icon use {
|
||||
fill: #0074a6;
|
||||
}
|
||||
.dark .ctrl-btns div.refresh.active:hover svg.embeddedIcon g.icon use {
|
||||
fill: #ce5b58;
|
||||
}
|
||||
109
web/gui2/src/main/webapp/app/fw/widget/table.css
Normal file
109
web/gui2/src/main/webapp/app/fw/widget/table.css
Normal file
@ -0,0 +1,109 @@
|
||||
/*
|
||||
* Copyright 2015-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/* ------ for summary-list tables (layout) ------ */
|
||||
|
||||
div.summary-list {
|
||||
margin: 0 20px 16px 10px;
|
||||
font-size: 10pt;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
div.summary-list table {
|
||||
border-collapse: collapse;
|
||||
table-layout: fixed;
|
||||
empty-cells: show;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div.summary-list div.table-body {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
div.summary-list div.table-body::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.summary-list tr.no-data td {
|
||||
text-align: center;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
/* highlighting */
|
||||
div.summary-list tr {
|
||||
transition: background-color 500ms;
|
||||
}
|
||||
|
||||
div.summary-list td {
|
||||
padding: 4px;
|
||||
text-align: left;
|
||||
word-wrap: break-word;
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
div.summary-list td.table-icon {
|
||||
width: 42px;
|
||||
padding-top: 4px;
|
||||
padding-bottom: 0px;
|
||||
padding-left: 4px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
div.summary-list .table-header th {
|
||||
font-weight: bold;
|
||||
font-variant: small-caps;
|
||||
text-transform: uppercase;
|
||||
font-size: 10pt;
|
||||
padding-top: 8px;
|
||||
padding-bottom: 8px;
|
||||
letter-spacing: 0.02em;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* rows are selectable */
|
||||
div.summary-list .table-body td {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Tabular view controls */
|
||||
|
||||
div.tabular-header .search {
|
||||
margin: 0 0 10px 10px;
|
||||
}
|
||||
|
||||
|
||||
div.tabular-header div.ctrl-btns {
|
||||
display: inline-block;
|
||||
float: right;
|
||||
height: 44px;
|
||||
margin-top: 24px;
|
||||
margin-right: 20px;
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
}
|
||||
|
||||
div.tabular-header div.ctrl-btns div {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.tabular-header div.ctrl-btns div.separator {
|
||||
width: 0;
|
||||
height: 40px;
|
||||
padding: 0;
|
||||
border-right: 1px solid #c7c7c0;
|
||||
}
|
||||
215
web/gui2/src/main/webapp/app/fw/widget/tablebase.ts
Normal file
215
web/gui2/src/main/webapp/app/fw/widget/tablebase.ts
Normal file
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* Copyright 2015-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FnService } from '../util/fn.service';
|
||||
import { LoadingService } from '../layer/loading.service';
|
||||
import { LogService } from '../../log.service';
|
||||
import { WebSocketService } from '../remote/websocket.service';
|
||||
|
||||
const REFRESH_INTERVAL = 2000;
|
||||
|
||||
/**
|
||||
* Base model of table view - implemented by Table components
|
||||
*/
|
||||
export interface TableBase {
|
||||
annots: TableAnnots;
|
||||
autoRefresh: boolean;
|
||||
autoRefreshTip: string;
|
||||
changedData: any;
|
||||
payloadParams: any;
|
||||
selId: string;
|
||||
sortParams: any;
|
||||
tableData: any[];
|
||||
toggleRefresh(): void;
|
||||
selectCallback(event: any, selRow: any): void;
|
||||
parentSelCb(event: any, selRow: any): void;
|
||||
sortCallback(): void;
|
||||
responseCallback(): void;
|
||||
}
|
||||
|
||||
interface TableAnnots {
|
||||
noRowsMsg: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* A model of data returned in a TableResponse
|
||||
*
|
||||
* There is an interface extending from this one in the parent component
|
||||
*/
|
||||
export interface TableResponse {
|
||||
annots: any;
|
||||
// There will be other parts to the response depending on table type
|
||||
// Expect one called tag+'s' e.g. devices or apps
|
||||
}
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Widget -- Table Base class
|
||||
*/
|
||||
export class TableBaseImpl implements TableBase {
|
||||
// attributes from the interface
|
||||
public annots: TableAnnots;
|
||||
autoRefresh: boolean = true;
|
||||
autoRefreshTip: string = 'Toggle auto refresh'; // TODO: get LION string
|
||||
changedData: string[] = [];
|
||||
payloadParams: any;
|
||||
selId: string = undefined;
|
||||
sortParams: any;
|
||||
tableData: any[] = [];
|
||||
toggleRefresh; // Function
|
||||
selectCallback; // Function
|
||||
parentSelCb = null;
|
||||
sortCallback; // Function
|
||||
responseCallback; // Function
|
||||
|
||||
private root: string;
|
||||
private req: string;
|
||||
private resp: string;
|
||||
private refreshPromise: any = null;
|
||||
private handlers: string[] = [];
|
||||
|
||||
constructor(
|
||||
protected fs: FnService,
|
||||
protected ls: LoadingService,
|
||||
protected log: LogService,
|
||||
protected wss: WebSocketService,
|
||||
protected tag: string,
|
||||
protected idKey: string = 'id',
|
||||
protected query: string = '',
|
||||
protected selCb = () => ({}) // Function
|
||||
) {
|
||||
this.root = tag + 's';
|
||||
this.req = tag + 'DataRequest';
|
||||
this.resp = tag + 'DataResponse';
|
||||
|
||||
this.sortCallback = this.requestTableData;
|
||||
this.selectCallback = this.rowSelectionCb;
|
||||
this.toggleRefresh = () => {
|
||||
this.autoRefresh = !this.autoRefresh;
|
||||
this.autoRefresh ? this.startRefresh() : this.stopRefresh();
|
||||
};
|
||||
}
|
||||
|
||||
init() {
|
||||
this.wss.bindHandlers(new Map<string, (data) => void>([
|
||||
[this.resp, (data) => this.tableDataResponseCb(data)]
|
||||
]));
|
||||
this.handlers.push(this.resp);
|
||||
|
||||
this.annots = <TableAnnots>{
|
||||
noRowsMsg: ''
|
||||
};
|
||||
|
||||
// Now send the WebSocket request and make it repeat every 2 seconds
|
||||
this.requestTableData();
|
||||
this.startRefresh();
|
||||
|
||||
this.log.debug('TableBase initialized');
|
||||
}
|
||||
|
||||
destroy() {
|
||||
this.wss.unbindHandlers(this.handlers);
|
||||
this.stopRefresh();
|
||||
this.ls.stop();
|
||||
}
|
||||
|
||||
/**
|
||||
* A callback that executes when the table data that was requested
|
||||
* on WebSocketService arrives.
|
||||
*
|
||||
* Happens every 2 seconds
|
||||
*/
|
||||
tableDataResponseCb(data: TableResponse) {
|
||||
this.ls.stop();
|
||||
|
||||
const newTableData: any[] = Array.from(data[this.root]);
|
||||
this.annots.noRowsMsg = data.annots.no_rows_msg;
|
||||
|
||||
// If the onResp() function is set then call it
|
||||
if (this.responseCallback) {
|
||||
this.responseCallback(data);
|
||||
}
|
||||
this.changedData = [];
|
||||
|
||||
// checks if data changed for row flashing
|
||||
if (JSON.stringify(newTableData) !== JSON.stringify(this.tableData)) {
|
||||
this.log.debug('table data has changed');
|
||||
const oldTableData: any[] = this.tableData;
|
||||
this.tableData = [ ...newTableData ]; // ES6 spread syntax
|
||||
// only flash the row if the data already exists
|
||||
if (oldTableData.length > 0) {
|
||||
for (const idx in newTableData) {
|
||||
if (!this.fs.containsObj(oldTableData, newTableData[idx])) {
|
||||
this.changedData.push(newTableData[idx][this.idKey]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Table Data Request
|
||||
*/
|
||||
requestTableData() {
|
||||
const p = Object.assign({}, this.sortParams, this.payloadParams, this.query);
|
||||
|
||||
// Allow it to sit in pending events
|
||||
if (this.wss.isConnected()) {
|
||||
if (this.fs.debugOn('table')) {
|
||||
this.log.debug('Table data REQUEST:', this.req, p);
|
||||
}
|
||||
this.wss.sendEvent(this.req, p);
|
||||
this.ls.start();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Row Selected
|
||||
*/
|
||||
rowSelectionCb(event: any, selRow: any) {
|
||||
const selId: string = selRow[this.idKey];
|
||||
this.selId = (this.selId === selId) ? undefined : selId;
|
||||
if (this.parentSelCb) {
|
||||
this.log.debug('Parent called on Row', selId, 'selected');
|
||||
this.parentSelCb(event, selRow);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* autoRefresh functions
|
||||
*/
|
||||
startRefresh() {
|
||||
this.refreshPromise =
|
||||
setInterval(() => {
|
||||
if (!this.ls.waiting()) {
|
||||
if (this.fs.debugOn('table')) {
|
||||
this.log.debug('Refreshing ' + this.root + ' page');
|
||||
}
|
||||
this.requestTableData();
|
||||
}
|
||||
}, REFRESH_INTERVAL);
|
||||
}
|
||||
|
||||
stopRefresh() {
|
||||
if (this.refreshPromise) {
|
||||
clearInterval(this.refreshPromise);
|
||||
this.refreshPromise = null;
|
||||
}
|
||||
}
|
||||
|
||||
isChanged(id: string): boolean {
|
||||
return (this.fs.inArray(id, this.changedData) === -1) ? false : true;
|
||||
}
|
||||
}
|
||||
@ -1,37 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Injectable } from '@angular/core';
|
||||
import { FnService } from '../util/fn.service';
|
||||
import { LoadingService } from '../layer/loading.service';
|
||||
import { LogService } from '../../log.service';
|
||||
import { WebSocketService } from '../remote/websocket.service';
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Widget -- Table Builder Service
|
||||
*/
|
||||
@Injectable()
|
||||
export class TableBuilderService {
|
||||
|
||||
constructor(
|
||||
private fs: FnService,
|
||||
private ls: LoadingService,
|
||||
private log: LogService,
|
||||
private wss: WebSocketService
|
||||
) {
|
||||
this.log.debug('TableBuilderService constructed');
|
||||
}
|
||||
|
||||
}
|
||||
@ -23,11 +23,8 @@ import { UtilModule } from '../util/util.module';
|
||||
import { ButtonService } from './button.service';
|
||||
import { ChartBuilderService } from './chartbuilder.service';
|
||||
import { ListService } from './list.service';
|
||||
import { TableBuilderService } from './tablebuilder.service';
|
||||
import { TableDetailService } from './tabledetail.service';
|
||||
import { ToolbarService } from './toolbar.service';
|
||||
import { TooltipService } from './tooltip.service';
|
||||
import { TooltipDirective } from './tooltip.directive';
|
||||
import { SortableHeaderDirective } from './sortableheader.directive';
|
||||
import { TableResizeDirective } from './tableresize.directive';
|
||||
import { FlashChangesDirective } from './flashchanges.directive';
|
||||
@ -42,7 +39,6 @@ import { FlashChangesDirective } from './flashchanges.directive';
|
||||
// It's enough to import them in the OnosModule
|
||||
],
|
||||
declarations: [
|
||||
TooltipDirective,
|
||||
SortableHeaderDirective,
|
||||
TableResizeDirective,
|
||||
FlashChangesDirective
|
||||
@ -51,9 +47,7 @@ import { FlashChangesDirective } from './flashchanges.directive';
|
||||
ButtonService,
|
||||
ChartBuilderService,
|
||||
ListService,
|
||||
TableBuilderService,
|
||||
TableDetailService,
|
||||
TooltipService,
|
||||
ToolbarService
|
||||
]
|
||||
})
|
||||
|
||||
@ -27,8 +27,4 @@
|
||||
#view h2 {
|
||||
-webkit-margin-before: 0;
|
||||
-webkit-margin-after: 0;
|
||||
margin: 32px 0 4px 16px;
|
||||
padding: 0;
|
||||
font-size: 18pt;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<div style="text-align:center" onosDetectBrowser>
|
||||
<div id="view" onosDetectBrowser>
|
||||
<onos-mast></onos-mast>
|
||||
<onos-nav></onos-nav>
|
||||
<onos-veil #veil></onos-veil>
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { LionService } from './fw/util/lion.service';
|
||||
import { LogService } from './log.service';
|
||||
import { KeyService } from './fw/util/key.service';
|
||||
@ -62,7 +62,7 @@ function cap(s: string): string {
|
||||
templateUrl: './onos.component.html',
|
||||
styleUrls: ['./onos.component.css', './onos.common.css']
|
||||
})
|
||||
export class OnosComponent implements OnInit {
|
||||
export class OnosComponent implements OnInit, OnDestroy {
|
||||
public title = 'onos';
|
||||
|
||||
// view ID to help page url map.. injected via the servlet
|
||||
@ -110,14 +110,19 @@ export class OnosComponent implements OnInit {
|
||||
|
||||
this.onos.viewMap = this.viewMap;
|
||||
|
||||
// this.wss.createWebSocket({
|
||||
// wsport: Window.location.search().wsport
|
||||
// });
|
||||
|
||||
// TODO: Enable this this.saucy(this.ee, this.ks);
|
||||
this.log.debug('OnosComponent initialized');
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
if (this.wss.isConnected()) {
|
||||
this.log.debug('Stopping Web Socket connection');
|
||||
this.wss.closeWebSocket();
|
||||
}
|
||||
|
||||
this.log.debug('OnosComponent destroyed');
|
||||
}
|
||||
|
||||
saucy(ee, ks) {
|
||||
const map = ee.genMap(sauce);
|
||||
Object.keys(map).forEach(function (k) {
|
||||
|
||||
@ -1,3 +1,100 @@
|
||||
<div id="ov-app">
|
||||
<p>apps works!</p>
|
||||
</div>
|
||||
<div id="ov-app" filedrop on-file-drop="appDropped()">
|
||||
<div class="tabular-header">
|
||||
<h2>
|
||||
{{lionFn('title_apps')}}
|
||||
({{ tableData.length }}
|
||||
{{ lionFn('total') }})
|
||||
</h2>
|
||||
<div class="ctrl-btns">
|
||||
<div class="refresh" (click)="toggleRefresh()">
|
||||
<onos-icon classes="{{ autoRefresh?'active refresh':'refresh' }}"
|
||||
iconId="refresh" iconSize="42" toolTip="{{ autoRefreshTip }}"></onos-icon>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
|
||||
<!--<form id="inputFileForm">-->
|
||||
<!--<input id="uploadFile"-->
|
||||
<!--type="file" size="50" accept=".oar,.jar"-->
|
||||
<!--file-model="appFile">-->
|
||||
<!--</form>-->
|
||||
|
||||
<div class="active" trigger-form>
|
||||
<onos-icon classes="{{ 'active upload' }}"
|
||||
iconId="upload" iconSize="42" toolTip="{{ uploadTip }}"></onos-icon>
|
||||
</div>
|
||||
<div (click)="appAction('activate')">
|
||||
<onos-icon classes="{{ ctrlBtnState.installed?'active play':'play' }}"
|
||||
iconId="play" iconSize="42" toolTip="{{ activateTip }}"></onos-icon>
|
||||
</div>
|
||||
<div (click)="appAction('deactivate')">
|
||||
<onos-icon classes="{{ ctrlBtnState.active?'active stop':'stop' }}"
|
||||
iconId="stop" iconSize="42" toolTip="{{ deactivateTip }}"></onos-icon>
|
||||
</div>
|
||||
<div (click)="appAction('uninstall')">
|
||||
<!--[ngClass]="{active: ctrlBtnState.selection}">-->
|
||||
<!--tooltip tt-msg="uninstallTip"-->
|
||||
<onos-icon classes="{{ ctrlBtnState.selection?'active garbage':'garbage' }}"
|
||||
iconId="garbage" iconSize="42" toolTip="{{ uninstallTip }}"></onos-icon>
|
||||
</div>
|
||||
<div (click)="downloadApp()">
|
||||
<onos-icon classes="{{ ctrlBtnState.selection?'active download':'download' }}"
|
||||
iconId="download" iconSize="42" toolTip="{{ downloadTip }}"></onos-icon>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!--<div class="search">-->
|
||||
<!--<input type="text" ng-model="queryTxt" placeholder="Search"/>-->
|
||||
<!--<select ng-model="queryBy">-->
|
||||
<!--<option value="" disabled>Search By</option>-->
|
||||
<!--<option value="$">All Fields</option>-->
|
||||
<!--<option value="title">{{lionFn('title')}}</option>-->
|
||||
<!--<option value="id">{{lionFn('app_id')}}</option>-->
|
||||
<!--<option value="version">{{lionFn('version')}}</option>-->
|
||||
<!--<option value="category">{{lionFn('category')}}</option>-->
|
||||
<!--<option value="apporiginName">{{lionFn('origin')}}</option>-->
|
||||
|
||||
<!--</select>-->
|
||||
<!--</div>-->
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
<div class="summary-list" onos-table-resize>
|
||||
<table onos-flash-changes id-prop="id" width="100%">
|
||||
<tr class="table-header">
|
||||
<th colId="state" class="table-icon" sortable></th>
|
||||
<th colId="icon" class="table-icon"></th>
|
||||
<th colId="title" [ngClass]="{width: '340'}" sortable> {{lionFn('title')}} </th>
|
||||
<th colId="id" [ngClass]="{width: '320px'}"sortable> {{lionFn('app_id')}} </th>
|
||||
<th colId="version" [ngClass]="{width: '140px'}"sortable> {{lionFn('version')}} </th>
|
||||
<th colId="category" [ngClass]="{width: '136px'}"sortable> {{lionFn('category')}} </th>
|
||||
<th colId="origin" sortable> {{lionFn('origin')}} </th>
|
||||
</tr>
|
||||
|
||||
<tr *ngIf="tableData.length === 0" class="no-data">
|
||||
<td colspan="5">
|
||||
{{annots.no_rows_msg}}
|
||||
</td>
|
||||
</tr>
|
||||
<!--<!–TODO: Add back in | filter:queryFilter–>-->
|
||||
<tr class="table-body" *ngFor="let app of tableData; trackBy $index"
|
||||
(click)="selectCallback($event, app)"
|
||||
[ngClass]="{selected: app.id === selId, 'data-change': isChanged(app.id)}">
|
||||
<td class="table-icon">
|
||||
<onos-icon iconId="{{app._iconid_state}}"></onos-icon>
|
||||
</td>
|
||||
<td class="table-icon">
|
||||
<!--<img data-ng-src="./rs/applications/{{app.icon}}/icon"-->
|
||||
<!--height="24px" width="24px" />-->
|
||||
</td>
|
||||
<td>{{ app.title }}</td>
|
||||
<td>{{ app.id }}</td>
|
||||
<td>{{ app.version }}</td>
|
||||
<td>{{ app.category }}</td>
|
||||
<td>{{ app.origin }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@ -13,46 +13,235 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { DialogService } from '../../fw/layer/dialog.service';
|
||||
import { FnService } from '../../fw/util/fn.service';
|
||||
import { IconService } from '../../fw/svg/icon.service';
|
||||
import { KeyService } from '../../fw/util/key.service';
|
||||
import { LionService } from '../../fw/util/lion.service';
|
||||
import { LoadingService } from '../../fw/layer/loading.service';
|
||||
import { LogService } from '../../log.service';
|
||||
import { PanelService } from '../../fw/layer/panel.service';
|
||||
import { TableBuilderService } from '../../fw/widget/tablebuilder.service';
|
||||
import { TableBaseImpl, TableResponse } from '../../fw/widget/tablebase';
|
||||
import { UrlFnService } from '../../fw/remote/urlfn.service';
|
||||
import { WebSocketService } from '../../fw/remote/websocket.service';
|
||||
|
||||
const INSTALLED = 'INSTALLED';
|
||||
const ACTIVE = 'ACTIVE';
|
||||
const appMgmtReq = 'appManagementRequest';
|
||||
const topPdg = 60;
|
||||
const panelWidth = 540;
|
||||
const pName = 'application-details-panel';
|
||||
const detailsReq = 'appDetailsRequest';
|
||||
const detailsResp = 'appDetailsResponse';
|
||||
const fileUploadUrl = 'applications/upload';
|
||||
const activateOption = '?activate=true';
|
||||
const appUrlPrefix = 'rs/applications/';
|
||||
const iconUrlSuffix = '/icon';
|
||||
const downloadSuffix = '/download';
|
||||
const dialogId = 'app-dialog';
|
||||
const dialogOpts = {
|
||||
edge: 'right',
|
||||
width: 400,
|
||||
};
|
||||
const strongWarning = {
|
||||
'org.onosproject.drivers': true,
|
||||
};
|
||||
const propOrder = ['id', 'state', 'category', 'version', 'origin', 'role'];
|
||||
|
||||
interface AppTableResponse extends TableResponse {
|
||||
apps: Apps[];
|
||||
}
|
||||
|
||||
interface Apps {
|
||||
category: string;
|
||||
desc: string;
|
||||
features: string;
|
||||
icon: string;
|
||||
id: string;
|
||||
origin: string;
|
||||
permissions: string;
|
||||
readme: string;
|
||||
required_apps: string;
|
||||
role: string;
|
||||
state: string;
|
||||
title: string;
|
||||
url: string;
|
||||
version: string;
|
||||
_iconid_state: string;
|
||||
}
|
||||
|
||||
interface CtrlBtnState {
|
||||
installed: boolean;
|
||||
selection: string;
|
||||
active: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Apps View Component
|
||||
*/
|
||||
@Component({
|
||||
selector: 'onos-apps',
|
||||
templateUrl: './apps.component.html',
|
||||
styleUrls: ['./apps.component.css']
|
||||
styleUrls: [
|
||||
'./apps.component.css', './apps.theme.css',
|
||||
'../../fw/widget/table.css', '../../fw/widget/table-theme.css'
|
||||
]
|
||||
})
|
||||
export class AppsComponent implements OnInit {
|
||||
export class AppsComponent extends TableBaseImpl implements OnInit, OnDestroy {
|
||||
|
||||
// deferred localization strings
|
||||
lionFn; // Function
|
||||
warnDeactivate: string;
|
||||
warnOwnRisk: string;
|
||||
friendlyProps: string[];
|
||||
ctrlBtnState: CtrlBtnState;
|
||||
detailsPanel: any;
|
||||
|
||||
constructor(
|
||||
private fs: FnService,
|
||||
protected fs: FnService,
|
||||
private ds: DialogService,
|
||||
private is: IconService,
|
||||
private ks: KeyService,
|
||||
private ls: LionService,
|
||||
private log: LogService,
|
||||
private lion: LionService,
|
||||
protected ls: LoadingService,
|
||||
protected log: LogService,
|
||||
private ps: PanelService,
|
||||
private tbs: TableBuilderService,
|
||||
private ufs: UrlFnService,
|
||||
private wss: WebSocketService,
|
||||
private window: Window
|
||||
protected wss: WebSocketService,
|
||||
private window: Window,
|
||||
) {
|
||||
this.log.debug('AppsComponent constructed');
|
||||
super(fs, null, log, wss, 'app');
|
||||
this.responseCallback = this.appResponseCb;
|
||||
this.sortParams = {
|
||||
firstCol: 'state',
|
||||
firstDir: 'desc',
|
||||
secondCol: 'title',
|
||||
secondDir: 'asc',
|
||||
};
|
||||
// We want doLion() to be called only after the Lion service is populated (from the WebSocket)
|
||||
this.lion.loadCb = (() => this.doLion());
|
||||
this.ctrlBtnState = <CtrlBtnState>{
|
||||
installed: false,
|
||||
active: false
|
||||
};
|
||||
if (this.lion.ubercache.length === 0) {
|
||||
this.lionFn = this.dummyLion;
|
||||
} else {
|
||||
this.doLion();
|
||||
}
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.log.debug('AppsComponent initialized');
|
||||
this.init();
|
||||
this.log.debug('AppComponent initialized');
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy();
|
||||
this.log.debug('AppComponent destroyed');
|
||||
}
|
||||
|
||||
/**
|
||||
* The callback called when App data returns from WSS
|
||||
*/
|
||||
appResponseCb(data: AppTableResponse) {
|
||||
this.log.debug('App response received for ', data.apps.length, 'apps');
|
||||
}
|
||||
|
||||
refreshCtrls() {
|
||||
let row;
|
||||
let rowIdx;
|
||||
if (this.ctrlBtnState.selection) {
|
||||
rowIdx = this.fs.find(this.selId, this.tableData);
|
||||
row = rowIdx >= 0 ? this.tableData[rowIdx] : null;
|
||||
|
||||
this.ctrlBtnState.installed = row && row.state === INSTALLED;
|
||||
this.ctrlBtnState.active = row && row.state === ACTIVE;
|
||||
} else {
|
||||
this.ctrlBtnState.installed = false;
|
||||
this.ctrlBtnState.active = false;
|
||||
}
|
||||
}
|
||||
|
||||
createConfirmationText(action, itemId) {
|
||||
// let content = this.ds.createDiv();
|
||||
// content.append('p').text(this.lionFn(action) + ' ' + itemId);
|
||||
// if (strongWarning[itemId]) {
|
||||
// content.append('p').html(
|
||||
// this.fs.sanitize(this.warnDeactivate) +
|
||||
// '<br>' +
|
||||
// this.fs.sanitize(this.warnOwnRisk)
|
||||
// ).classed('strong', true);
|
||||
// }
|
||||
// return content;
|
||||
}
|
||||
|
||||
confirmAction(action): void {
|
||||
const itemId = this.selId;
|
||||
const spar = this.sortParams;
|
||||
|
||||
function dOk() {
|
||||
this.log.debug('Initiating', action, 'of', itemId);
|
||||
this.wss.sendEvent(appMgmtReq, {
|
||||
action: action,
|
||||
name: itemId,
|
||||
sortCol: spar.sortCol,
|
||||
sortDir: spar.sortDir,
|
||||
});
|
||||
if (action === 'uninstall') {
|
||||
this.detailsPanel.hide();
|
||||
} else {
|
||||
this.wss.sendEvent(detailsReq, { id: itemId });
|
||||
}
|
||||
}
|
||||
|
||||
function dCancel() {
|
||||
this.log.debug('Canceling', action, 'of', itemId);
|
||||
}
|
||||
|
||||
// this.ds.openDialog(dialogId, dialogOpts)
|
||||
// .setTitle(this.lionFn('dlg_confirm_action'))
|
||||
// .addContent(this.createConfirmationText(action, itemId))
|
||||
// .addOk(dOk)
|
||||
// .addCancel(dCancel)
|
||||
// .bindKeys();
|
||||
}
|
||||
|
||||
appAction(action) {
|
||||
if (this.ctrlBtnState.selection) {
|
||||
this.confirmAction(action);
|
||||
}
|
||||
}
|
||||
|
||||
downloadApp() {
|
||||
if (this.ctrlBtnState.selection) {
|
||||
(<any>this.window).location = appUrlPrefix + this.selId + downloadSuffix;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the LION bundle for App - this should replace the dummyLion implementation
|
||||
* of lionFn with a function from the LION Service
|
||||
*/
|
||||
doLion() {
|
||||
this.lionFn = this.lion.bundle('core.view.App');
|
||||
|
||||
this.warnDeactivate = this.lionFn('dlg_warn_deactivate');
|
||||
this.warnOwnRisk = this.lionFn('dlg_warn_own_risk');
|
||||
|
||||
this.friendlyProps = [
|
||||
this.lionFn('app_id'), this.lionFn('state'),
|
||||
this.lionFn('category'), this.lionFn('version'),
|
||||
this.lionFn('origin'), this.lionFn('role'),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* A dummy implementation of the lionFn until the response is received and the LION
|
||||
* bundle is received from the WebSocket
|
||||
*/
|
||||
dummyLion(key: string): string {
|
||||
return '%' + key + '%';
|
||||
}
|
||||
}
|
||||
|
||||
@ -17,6 +17,7 @@ import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { AppsComponent } from './apps.component';
|
||||
import { TriggerFormDirective } from './triggerform.directive';
|
||||
import { SvgModule } from '../../fw/svg/svg.module';
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Apps View Module
|
||||
@ -30,7 +31,8 @@ import { TriggerFormDirective } from './triggerform.directive';
|
||||
AppsComponent
|
||||
],
|
||||
imports: [
|
||||
CommonModule
|
||||
CommonModule,
|
||||
SvgModule
|
||||
],
|
||||
declarations: [
|
||||
AppsComponent,
|
||||
|
||||
@ -17,12 +17,15 @@
|
||||
/*
|
||||
ONOS GUI -- Device View (layout) -- CSS file
|
||||
*/
|
||||
#ov-device .tabular-header {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
#ov-device h2 {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
#ov-device div.ctrl-btns {
|
||||
#ov-device, div.ctrl-btns {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@ -1,3 +1,99 @@
|
||||
<div id="ov-device">
|
||||
<p>device works!</p>
|
||||
<div class="tabular-header">
|
||||
<h2>Devices ({{ tableData.length }} total)</h2>
|
||||
<div class="ctrl-btns">
|
||||
<div class="refresh" (click)="toggleRefresh()">
|
||||
<!-- See icon.theme.css for the defintions of the classes active and refresh-->
|
||||
<onos-icon classes="{{ autoRefresh?'active refresh':'refresh' }}"
|
||||
iconId="refresh" iconSize="42" toolTip="{{ autoRefreshTip }}"></onos-icon>
|
||||
</div>
|
||||
<div class="separator"></div>
|
||||
|
||||
<div>
|
||||
<onos-icon classes="{{ selId ? 'current-view':undefined }}"
|
||||
iconId="deviceTable" iconSize="42"></onos-icon>
|
||||
</div>
|
||||
|
||||
<div routerLink="/flow" routerLinkActive="active">
|
||||
<onos-icon classes="{{ selId ? 'active':undefined }}"
|
||||
iconId="flowTable" iconSize="42" toolTip="{{ flowTip }}"></onos-icon>
|
||||
</div>
|
||||
|
||||
<div routerLink="/port" routerLinkActive="active">
|
||||
<onos-icon classes="{{ selId ? 'active':undefined }}"
|
||||
iconId="portTable" iconSize="42" toolTip="{{ portTip }}"></onos-icon>
|
||||
</div>
|
||||
|
||||
<div routerLink="/group" routerLinkActive="active">
|
||||
<onos-icon classes="{{ selId ? 'active':undefined }}"
|
||||
iconId="groupTable" iconSize="42" toolTip="{{ groupTip }}"></onos-icon>
|
||||
</div>
|
||||
|
||||
<div routerLink="/meter" routerLinkActive="active">
|
||||
<onos-icon classes="{{ selId ? 'active':undefined }}"
|
||||
iconId="meterTable" iconSize="42" toolTip="{{ meterTip }}"></onos-icon>
|
||||
</div>
|
||||
|
||||
<div routerLink="/pipeconf" routerLinkActive="active">
|
||||
<onos-icon classes="{{ selId ? 'active':undefined }}"
|
||||
iconId="pipeconfTable" iconSize="42" toolTip="{{ pipeconfTip }}"></onos-icon>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="summary-list" onos-table-resize>
|
||||
<table onos-flash-changes id-prop="id" width="100%">
|
||||
<tr class="table-header">
|
||||
<th colId="available" class="table-icon" sortable></th>
|
||||
<th colId="type" class="table-icon"></th>
|
||||
<th colId="name" sortable>Friendly Name </th>
|
||||
<th colId="id" sortable>Device ID </th>
|
||||
<th colId="masterid" [ngClass]="{width: '130px'}" sortable>Master </th>
|
||||
<th colId="num_ports" [ngClass]="{width: '70px'}" sortable>Ports </th>
|
||||
<th colId="mfr" sortable>Vendor </th>
|
||||
<th colId="hw" sortable>H/W Version </th>
|
||||
<th colId="sw" sortable>S/W Version </th>
|
||||
<th colId="protocol" [ngClass]="{width: '100px'}" sortable>Protocol </th>
|
||||
</tr>
|
||||
|
||||
<tr class="table-body" *ngIf="tableData.length === 0" class="no-data">
|
||||
<td colspan="9">{{ annots.noRowsMsg }}</td>
|
||||
</tr>
|
||||
|
||||
|
||||
<tr class="table-body" *ngFor="let dev of tableData; trackBy $index"
|
||||
(click)="selectCallback($event, dev)"
|
||||
[ngClass]="{selected: dev.id === selId, 'data-change': isChanged(dev.id)}">
|
||||
<td class="table-icon">
|
||||
<!--[ngClass]="{width: devAvail.getBBox().width}"-->
|
||||
<onos-icon iconId="{{dev._iconid_available}}"></onos-icon>
|
||||
</td>
|
||||
<td class="table-icon">
|
||||
<onos-icon iconId="{{dev._iconid_type}}"></onos-icon>
|
||||
</td>
|
||||
<td>{{ dev.name }}</td>
|
||||
<td>{{ dev.id }}</td>
|
||||
<td>{{ dev.masterid }}</td>
|
||||
<td>{{ dev.num_ports }}</td>
|
||||
<td>{{ dev.mfr }}</td>
|
||||
<td>{{ dev.hw }}</td>
|
||||
<td>{{ dev.sw }}</td>
|
||||
<td>{{ dev.protocol }}</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<small>
|
||||
<p>TODO (21 Jun 18): Add in:</p>
|
||||
<ul>
|
||||
<li>Scrolling for long lists of devices</li>
|
||||
<li>Sorting by column</li>
|
||||
<li>Left align header columns</li>
|
||||
<li>Move tooltip to underneath icon</li>
|
||||
<li>Correct width and icon colour of active and device icon columns</li>
|
||||
<li>Add device details panel</li>
|
||||
<li>Add more unit tests</li>
|
||||
<li>Make icon for #undefined work (e.g. for device type olt or unknown)</li>
|
||||
<li>Change loading service to fade in and out and have a threshold of </li>
|
||||
</ul>
|
||||
</small>
|
||||
</div>
|
||||
|
||||
@ -13,7 +13,7 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { Component, OnInit, OnDestroy } from '@angular/core';
|
||||
import { DetailsPanelService } from '../../fw/layer/detailspanel.service';
|
||||
import { FnService } from '../../fw/util/fn.service';
|
||||
import { IconService } from '../../fw/svg/icon.service';
|
||||
@ -23,42 +23,78 @@ import { LogService } from '../../log.service';
|
||||
import { MastService } from '../../fw/mast/mast.service';
|
||||
import { NavService } from '../../fw/nav/nav.service';
|
||||
import { PanelService } from '../../fw/layer/panel.service';
|
||||
import { TableBuilderService } from '../../fw/widget/tablebuilder.service';
|
||||
import { TableBaseImpl, TableResponse } from '../../fw/widget/tablebase';
|
||||
import { TableDetailService } from '../../fw/widget/tabledetail.service';
|
||||
import { WebSocketService } from '../../fw/remote/websocket.service';
|
||||
|
||||
interface DeviceTableResponse extends TableResponse {
|
||||
devices: Device[];
|
||||
}
|
||||
|
||||
interface Device {
|
||||
available: boolean;
|
||||
chassisid: string;
|
||||
hw: string;
|
||||
id: string;
|
||||
masterid: string;
|
||||
mfr: string;
|
||||
name: string;
|
||||
num_ports: number;
|
||||
protocol: string;
|
||||
serial: string;
|
||||
sw: string;
|
||||
_iconid_available: string;
|
||||
_iconid_type: string;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Device View Component
|
||||
*/
|
||||
@Component({
|
||||
selector: 'onos-device',
|
||||
templateUrl: './device.component.html',
|
||||
styleUrls: ['./device.component.css']
|
||||
styleUrls: ['./device.component.css', './device.theme.css', '../../fw/widget/table.css', '../../fw/widget/table-theme.css']
|
||||
})
|
||||
export class DeviceComponent implements OnInit {
|
||||
export class DeviceComponent extends TableBaseImpl implements OnInit, OnDestroy {
|
||||
|
||||
// TODO: Update for LION
|
||||
flowTip = 'Show flow view for selected device';
|
||||
portTip = 'Show port view for selected device';
|
||||
groupTip = 'Show group view for selected device';
|
||||
meterTip = 'Show meter view for selected device';
|
||||
pipeconfTip = 'Show pipeconf view for selected device';
|
||||
|
||||
constructor(
|
||||
private dps: DetailsPanelService,
|
||||
private fs: FnService,
|
||||
protected fs: FnService,
|
||||
protected ls: LoadingService,
|
||||
private is: IconService,
|
||||
private ks: KeyService,
|
||||
private log: LogService,
|
||||
protected log: LogService,
|
||||
private mast: MastService,
|
||||
private nav: NavService,
|
||||
private ps: PanelService,
|
||||
private tbs: TableBuilderService,
|
||||
private tds: TableDetailService,
|
||||
private wss: WebSocketService,
|
||||
private ls: LoadingService, // TODO: Remove - already added through tbs
|
||||
private window: Window
|
||||
protected wss: WebSocketService,
|
||||
private window: Window,
|
||||
) {
|
||||
this.log.debug('DeviceComponent constructed');
|
||||
super(fs, ls, log, wss, 'device');
|
||||
this.responseCallback = this.deviceResponseCb;
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.init();
|
||||
this.log.debug('DeviceComponent initialized');
|
||||
// TODO: Remove this - it's only for demo purposes
|
||||
// this.ls.startAnim();
|
||||
}
|
||||
|
||||
ngOnDestroy() {
|
||||
this.destroy();
|
||||
this.log.debug('DeviceComponent destroyed');
|
||||
}
|
||||
|
||||
deviceResponseCb(data: DeviceTableResponse) {
|
||||
this.log.debug('Device response received for ', data.devices.length, 'devices');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -15,9 +15,11 @@
|
||||
*/
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { DeviceComponent } from './device.component';
|
||||
import { DeviceDetailsPanelDirective } from './devicedetailspanel.directive';
|
||||
import { RemoteModule } from '../../fw/remote/remote.module';
|
||||
import { SvgModule } from '../../fw/svg/svg.module';
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Device View Module
|
||||
*/
|
||||
@ -27,7 +29,8 @@ import { RemoteModule } from '../../fw/remote/remote.module';
|
||||
],
|
||||
imports: [
|
||||
CommonModule,
|
||||
RemoteModule
|
||||
RouterModule,
|
||||
SvgModule
|
||||
],
|
||||
declarations: [
|
||||
DeviceComponent,
|
||||
|
||||
@ -41,6 +41,10 @@ body {
|
||||
|
||||
#view h2 {
|
||||
color: #3c3a3a;
|
||||
margin: 32px 0 4px 16px;
|
||||
padding: 0;
|
||||
font-size: 18pt;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
@ -33,8 +33,6 @@ class MockActivatedRoute extends ActivatedRoute {
|
||||
|
||||
class MockGlyphService {}
|
||||
|
||||
class MockWSock {}
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Remote -- Web Socket Service - Unit Tests
|
||||
*/
|
||||
@ -103,7 +101,7 @@ describe('WebSocketService', () => {
|
||||
'noHandlersWarn', 'resetState',
|
||||
'createWebSocket', 'bindHandlers', 'unbindHandlers',
|
||||
'addOpenListener', 'removeOpenListener', 'sendEvent',
|
||||
'setVeilDelegate', 'setLoadingDelegate'
|
||||
'setVeilDelegate', 'setLoadingDelegate', 'isConnected', 'closeWebSocket'
|
||||
])).toBeTruthy();
|
||||
});
|
||||
|
||||
@ -228,9 +226,7 @@ describe('WebSocketService', () => {
|
||||
});
|
||||
|
||||
it('should warn if no arguments, unbindHandlers', () => {
|
||||
expect(wss.unbindHandlers(
|
||||
new Map<string, (data) => void>([])
|
||||
)).toBeNull();
|
||||
expect(wss.unbindHandlers([])).toBeNull();
|
||||
expect(logServiceSpy.warn).toHaveBeenCalledWith(
|
||||
'WSS.unbindHandlers(): no event handlers'
|
||||
);
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { LogService } from '../../../../app/log.service';
|
||||
import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
|
||||
import { WSock } from '../../../../app/fw/remote/wsock.service';
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Remote -- WSock Service - Unit Tests
|
||||
*/
|
||||
describe('WSock', () => {
|
||||
let log: LogService;
|
||||
|
||||
beforeEach(() => {
|
||||
log = new ConsoleLoggerService();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [WSock,
|
||||
{ provide: LogService, useValue: log },
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([WSock], (service: WSock) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
@ -1,62 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { ElementRef } from '@angular/core';
|
||||
import { LogService } from '../../../../app/log.service';
|
||||
import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
|
||||
import { IconDirective } from '../../../../app/fw/svg/icon.directive';
|
||||
import { IconService } from '../../../../app/fw/svg/icon.service';
|
||||
import { GlyphService } from '../../../../app/fw/svg/glyph.service';
|
||||
import { SvgUtilService } from '../../../../app/fw/svg/svgutil.service';
|
||||
import { FnService } from '../../../../app/fw//util/fn.service';
|
||||
import { ActivatedRoute, Router} from '@angular/router';
|
||||
|
||||
class MockFnService {}
|
||||
|
||||
class MockGlyphService {}
|
||||
|
||||
class MockIconService {}
|
||||
|
||||
/**
|
||||
* ONOS GUI -- SVG -- Icon Directive - Unit Tests
|
||||
*/
|
||||
describe('IconDirective', () => {
|
||||
let log: LogService;
|
||||
const elementMock = <any>{ };
|
||||
|
||||
beforeEach(() => {
|
||||
log = new ConsoleLoggerService();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [ IconDirective,
|
||||
{ provide: FnService, useClass: MockFnService },
|
||||
{ provide: LogService, useValue: log },
|
||||
{ provide: ElementRef, useValue: elementMock },
|
||||
{ provide: GlyphService, useClass: MockGlyphService },
|
||||
{ provide: IconService, useClass: MockIconService },
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
log = null;
|
||||
});
|
||||
|
||||
it('should create an instance', inject([IconDirective], (directive: IconDirective) => {
|
||||
expect(directive).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
@ -235,7 +235,7 @@ describe('FnService', () => {
|
||||
'isFirefox', 'parseDebugFlags',
|
||||
'debugOn', 'debug', 'find', 'inArray', 'removeFromArray',
|
||||
'isEmptyObject', 'cap', 'noPx', 'noPxStyle', 'endsWith',
|
||||
'inEvilList', 'analyze', 'sanitize'
|
||||
'inEvilList', 'analyze', 'sanitize', 'sameObjProps', 'containsObj'
|
||||
// 'find', 'inArray', 'removeFromArray', 'isEmptyObject', 'sameObjProps', 'containsObj', 'cap',
|
||||
// 'eecode', 'noPx', 'noPxStyle', 'endsWith', 'addToTrie', 'removeFromTrie', 'trieLookup',
|
||||
// 'classNames', 'extend', 'sanitize'
|
||||
|
||||
@ -20,14 +20,11 @@ import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
|
||||
import { ButtonService } from '../../../../app/fw/widget/button.service';
|
||||
import { FnService } from '../../../../app/fw/util/fn.service';
|
||||
import { IconService } from '../../../../app/fw/svg/icon.service';
|
||||
import { TooltipService } from '../../../../app/fw/widget/tooltip.service';
|
||||
|
||||
class MockIconService {}
|
||||
|
||||
class MockFnService {}
|
||||
|
||||
class MockTooltipService {}
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Widget -- Button Service - Unit Tests
|
||||
*/
|
||||
@ -42,7 +39,6 @@ describe('ButtonService', () => {
|
||||
{ provide: LogService, useValue: log },
|
||||
{ provide: IconService, useClass: MockIconService },
|
||||
{ provide: FnService, useClass: MockFnService },
|
||||
{ provide: TooltipService, useClass: MockTooltipService },
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { LogService } from '../../../../app/log.service';
|
||||
import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
|
||||
import { TableBuilderService } from '../../../../app/fw/widget/tablebuilder.service';
|
||||
import { FnService } from '../../../../app/fw//util/fn.service';
|
||||
import { LoadingService } from '../../../../app/fw/layer/loading.service';
|
||||
import { WebSocketService } from '../../../../app/fw/remote/websocket.service';
|
||||
|
||||
class MockFnService {}
|
||||
|
||||
class MockLoadingService {}
|
||||
|
||||
class MockWebSocketService {}
|
||||
|
||||
/*
|
||||
ONOS GUI -- Widget -- Table Builder Service - Unit Tests
|
||||
*/
|
||||
describe('TableBuilderService', () => {
|
||||
let log: LogService;
|
||||
|
||||
beforeEach(() => {
|
||||
log = new ConsoleLoggerService();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [TableBuilderService,
|
||||
{ provide: FnService, useClass: MockFnService },
|
||||
{ provide: LoadingService, useClass: MockLoadingService },
|
||||
{ provide: LogService, useValue: log },
|
||||
{ provide: WebSocketService, useClass: MockWebSocketService },
|
||||
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([TableBuilderService], (service: TableBuilderService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
@ -1,49 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { TooltipDirective } from '../../../../app/fw/widget/tooltip.directive';
|
||||
import { LogService } from '../../../../app/log.service';
|
||||
import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
|
||||
import { FnService } from '../../../../app/fw/util/fn.service';
|
||||
|
||||
class MockFnService {}
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Widget -- Tooltip Directive - Unit Tests
|
||||
*/
|
||||
describe('TooltipDirective', () => {
|
||||
let log: LogService;
|
||||
|
||||
beforeEach(() => {
|
||||
log = new ConsoleLoggerService();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [ TooltipDirective,
|
||||
{ provide: FnService, useClass: MockFnService },
|
||||
{ provide: LogService, useValue: log },
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
log = null;
|
||||
});
|
||||
|
||||
it('should create an instance', inject([TooltipDirective], (directive: TooltipDirective) => {
|
||||
expect(directive).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
@ -1,45 +0,0 @@
|
||||
/*
|
||||
* Copyright 2015-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.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { TestBed, inject } from '@angular/core/testing';
|
||||
|
||||
import { LogService } from '../../../../app/log.service';
|
||||
import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
|
||||
import { TooltipService } from '../../../../app/fw/widget/tooltip.service';
|
||||
import { FnService } from '../../../../app/fw/util/fn.service';
|
||||
|
||||
class MockFnService {}
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Widget -- Tooltip Service - Unit Tests
|
||||
*/
|
||||
describe('TooltipService', () => {
|
||||
let log: LogService;
|
||||
|
||||
beforeEach(() => {
|
||||
log = new ConsoleLoggerService();
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [TooltipService,
|
||||
{ provide: LogService, useValue: log },
|
||||
{ provide: FnService, useClass: MockFnService },
|
||||
]
|
||||
});
|
||||
});
|
||||
|
||||
it('should be created', inject([TooltipService], (service: TooltipService) => {
|
||||
expect(service).toBeTruthy();
|
||||
}));
|
||||
});
|
||||
@ -59,6 +59,8 @@ class MockIconService {}
|
||||
|
||||
class MockKeyService {}
|
||||
|
||||
class MockLionService {}
|
||||
|
||||
class MockNavService {}
|
||||
|
||||
class MockOnosService {}
|
||||
@ -73,6 +75,11 @@ class MockThemeService {}
|
||||
|
||||
class MockVeilComponent {}
|
||||
|
||||
class MockWebSocketService {
|
||||
createWebSocket() {}
|
||||
isConnected() { return false; }
|
||||
}
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Onos Component - Unit Tests
|
||||
*/
|
||||
@ -81,6 +88,8 @@ describe('OnosComponent', () => {
|
||||
let fs: FnService;
|
||||
let ar: MockActivatedRoute;
|
||||
let windowMock: Window;
|
||||
let fixture;
|
||||
let app;
|
||||
|
||||
beforeEach(async(() => {
|
||||
log = new ConsoleLoggerService();
|
||||
@ -117,6 +126,7 @@ describe('OnosComponent', () => {
|
||||
{ provide: GlyphService, useClass: MockGlyphService },
|
||||
{ provide: IconService, useClass: MockIconService },
|
||||
{ provide: KeyService, useClass: MockKeyService },
|
||||
{ provide: LionService, useClass: MockLionService },
|
||||
{ provide: LogService, useValue: log },
|
||||
{ provide: NavService, useClass: MockNavService },
|
||||
{ provide: OnosService, useClass: MockOnosService },
|
||||
@ -124,20 +134,22 @@ describe('OnosComponent', () => {
|
||||
{ provide: PanelService, useClass: MockPanelService },
|
||||
{ provide: SpriteService, useClass: MockSpriteService },
|
||||
{ provide: ThemeService, useClass: MockThemeService },
|
||||
{ provide: WebSocketService, useClass: MockWebSocketService },
|
||||
{ provide: Window, useFactory: (() => windowMock ) },
|
||||
]
|
||||
}).compileComponents();
|
||||
|
||||
fixture = TestBed.createComponent(OnosComponent);
|
||||
app = fixture.componentInstance;
|
||||
}));
|
||||
|
||||
it('should create the app', async(() => {
|
||||
const fixture = TestBed.createComponent(OnosComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app).toBeTruthy();
|
||||
}));
|
||||
|
||||
it(`should have as title 'onos'`, async(() => {
|
||||
const fixture = TestBed.createComponent(OnosComponent);
|
||||
const app = fixture.debugElement.componentInstance;
|
||||
expect(app.title).toEqual('onos');
|
||||
}));
|
||||
// it(`should have as title 'onos'`, async(() => {
|
||||
// const fixture = TestBed.createComponent(OnosComponent);
|
||||
// const app = fixture.componentInstance;
|
||||
// expect(app.title).toEqual('onos');
|
||||
// }));
|
||||
});
|
||||
|
||||
@ -14,72 +14,126 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { LogService } from '../../../../app/log.service';
|
||||
import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
|
||||
import { AppsComponent } from '../../../../app/view/apps/apps.component';
|
||||
import { DialogService } from '../../../../app/fw/layer/dialog.service';
|
||||
import { FnService } from '../../../../app/fw/util/fn.service';
|
||||
import { IconComponent } from '../../../../app/fw/svg/icon/icon.component';
|
||||
import { IconService } from '../../../../app/fw/svg/icon.service';
|
||||
import { KeyService } from '../../../../app/fw/util/key.service';
|
||||
import { LionService } from '../../../../app/fw/util/lion.service';
|
||||
import { LoadingService } from '../../../../app/fw/layer/loading.service';
|
||||
import { PanelService } from '../../../../app/fw/layer/panel.service';
|
||||
import { TableBuilderService } from '../../../../app/fw/widget/tablebuilder.service';
|
||||
import { ThemeService } from '../../../../app/fw/util/theme.service';
|
||||
import { UrlFnService } from '../../../../app/fw/remote/urlfn.service';
|
||||
import { WebSocketService } from '../../../../app/fw/remote/websocket.service';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
class MockActivatedRoute extends ActivatedRoute {
|
||||
constructor(params: Params) {
|
||||
super();
|
||||
this.queryParams = of(params);
|
||||
}
|
||||
}
|
||||
|
||||
class MockDialogService {}
|
||||
|
||||
class MockFnService {}
|
||||
|
||||
class MockIconService {}
|
||||
class MockIconService {
|
||||
loadIconDef() {}
|
||||
}
|
||||
|
||||
class MockKeyService {}
|
||||
|
||||
class MockLionService {}
|
||||
class MockLoadingService {
|
||||
startAnim() {}
|
||||
stop() {}
|
||||
waiting() {}
|
||||
}
|
||||
|
||||
class MockPanelService {}
|
||||
|
||||
class MockTableBuilderService {}
|
||||
|
||||
class MockThemeService {}
|
||||
|
||||
class MockUrlFnService {}
|
||||
|
||||
class MockWebSocketService {}
|
||||
class MockWebSocketService {
|
||||
createWebSocket() {}
|
||||
isConnected() { return false; }
|
||||
unbindHandlers() {}
|
||||
bindHandlers() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Apps View -- Unit Tests
|
||||
*/
|
||||
describe('AppsComponent', () => {
|
||||
let log: LogService;
|
||||
let fs: FnService;
|
||||
let ar: MockActivatedRoute;
|
||||
let windowMock: Window;
|
||||
let logServiceSpy: jasmine.SpyObj<LogService>;
|
||||
let component: AppsComponent;
|
||||
let fixture: ComponentFixture<AppsComponent>;
|
||||
const windowMock = <any>{ location: <any> { hostname: 'localhost' } };
|
||||
const bundleObj = {
|
||||
'core.view.App': {
|
||||
test: 'test1'
|
||||
}
|
||||
};
|
||||
const mockLion = (key) => {
|
||||
return bundleObj[key] || '%' + key + '%';
|
||||
};
|
||||
|
||||
beforeEach(async(() => {
|
||||
log = new ConsoleLoggerService();
|
||||
const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
|
||||
ar = new MockActivatedRoute({'debug': 'txrx'});
|
||||
|
||||
windowMock = <any>{
|
||||
location: <any> {
|
||||
hostname: 'foo',
|
||||
host: 'foo',
|
||||
port: '80',
|
||||
protocol: 'http',
|
||||
search: { debug: 'true'},
|
||||
href: 'ws://foo:123/onos/ui/websock/path',
|
||||
absUrl: 'ws://foo:123/onos/ui/websock/path'
|
||||
}
|
||||
};
|
||||
fs = new FnService(ar, logSpy, windowMock);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ AppsComponent ],
|
||||
declarations: [ AppsComponent, IconComponent ],
|
||||
providers: [
|
||||
{ provide: DialogService, useClass: MockDialogService },
|
||||
{ provide: FnService, useClass: MockFnService },
|
||||
{ provide: FnService, useValue: fs },
|
||||
{ provide: IconService, useClass: MockIconService },
|
||||
{ provide: KeyService, useClass: MockKeyService },
|
||||
{ provide: LionService, useClass: MockLionService },
|
||||
{ provide: LogService, useValue: log },
|
||||
{ provide: LionService, useFactory: (() => {
|
||||
return {
|
||||
bundle: ((bundleId) => mockLion),
|
||||
ubercache: new Array()
|
||||
};
|
||||
})
|
||||
},
|
||||
{ provide: LoadingService, useClass: MockLoadingService },
|
||||
{ provide: LogService, useValue: logSpy },
|
||||
{ provide: PanelService, useClass: MockPanelService },
|
||||
{ provide: TableBuilderService, useClass: MockTableBuilderService },
|
||||
{ provide: ThemeService, useClass: MockThemeService },
|
||||
{ provide: UrlFnService, useClass: MockUrlFnService },
|
||||
{ provide: WebSocketService, useClass: MockWebSocketService },
|
||||
{ provide: Window, useValue: windowMock },
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
logServiceSpy = TestBed.get(LogService);
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(AppsComponent);
|
||||
component = fixture.componentInstance;
|
||||
component = fixture.debugElement.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
|
||||
@ -14,46 +14,50 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { ActivatedRoute, Params } from '@angular/router';
|
||||
import { DebugElement } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { LogService } from '../../../../app/log.service';
|
||||
import { ConsoleLoggerService } from '../../../../app/consolelogger.service';
|
||||
import { DeviceComponent } from '../../../../app/view/device/device.component';
|
||||
|
||||
import { DetailsPanelService } from '../../../../app/fw/layer/detailspanel.service';
|
||||
import { FnService, WindowSize } from '../../../../app/fw/util/fn.service';
|
||||
import { IconService } from '../../../../app/fw/svg/icon.service';
|
||||
import { GlyphService } from '../../../../app/fw/svg/glyph.service';
|
||||
import { IconComponent } from '../../../../app/fw/svg/icon/icon.component';
|
||||
import { KeyService } from '../../../../app/fw/util/key.service';
|
||||
import { LoadingService } from '../../../../app/fw/layer/loading.service';
|
||||
import { NavService } from '../../../../app/fw/nav/nav.service';
|
||||
import { MastService } from '../../../../app/fw/mast/mast.service';
|
||||
import { PanelService } from '../../../../app/fw/layer/panel.service';
|
||||
import { SvgUtilService } from '../../../../app/fw/svg/svgutil.service';
|
||||
import { TableBuilderService } from '../../../../app/fw/widget/tablebuilder.service';
|
||||
import { TableDetailService } from '../../../../app/fw/widget/tabledetail.service';
|
||||
import { ThemeService } from '../../../../app/fw/util/theme.service';
|
||||
import { WebSocketService } from '../../../../app/fw/remote/websocket.service';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
class MockDetailsPanelService {}
|
||||
|
||||
class MockFnService {
|
||||
windowSize(offH: number = 0, offW: number = 0): WindowSize {
|
||||
return {
|
||||
height: 123,
|
||||
width: 456
|
||||
};
|
||||
class MockActivatedRoute extends ActivatedRoute {
|
||||
constructor(params: Params) {
|
||||
super();
|
||||
this.queryParams = of(params);
|
||||
}
|
||||
}
|
||||
|
||||
class MockIconService {}
|
||||
class MockDetailsPanelService {}
|
||||
|
||||
class MockFnService {}
|
||||
|
||||
class MockIconService {
|
||||
loadIconDef() {}
|
||||
}
|
||||
|
||||
class MockGlyphService {}
|
||||
|
||||
class MockKeyService {}
|
||||
|
||||
class MockLoadingService {
|
||||
startAnim() {
|
||||
// Do nothing
|
||||
}
|
||||
startAnim() {}
|
||||
stop() {}
|
||||
}
|
||||
|
||||
class MockNavService {}
|
||||
@ -66,49 +70,81 @@ class MockTableBuilderService {}
|
||||
|
||||
class MockTableDetailService {}
|
||||
|
||||
class MockWebSocketService {}
|
||||
class MockThemeService {}
|
||||
|
||||
class MockWebSocketService {
|
||||
createWebSocket() {}
|
||||
isConnected() { return false; }
|
||||
unbindHandlers() {}
|
||||
bindHandlers() {}
|
||||
}
|
||||
|
||||
/**
|
||||
* ONOS GUI -- Device View Module - Unit Tests
|
||||
*/
|
||||
describe('DeviceComponent', () => {
|
||||
let log: LogService;
|
||||
let fs: FnService;
|
||||
let ar: MockActivatedRoute;
|
||||
let windowMock: Window;
|
||||
let logServiceSpy: jasmine.SpyObj<LogService>;
|
||||
let component: DeviceComponent;
|
||||
let fixture: ComponentFixture<DeviceComponent>;
|
||||
const windowMock = <any>{ location: <any> { hostname: 'localhost' } };
|
||||
|
||||
beforeEach(async(() => {
|
||||
log = new ConsoleLoggerService();
|
||||
const logSpy = jasmine.createSpyObj('LogService', ['info', 'debug', 'warn', 'error']);
|
||||
ar = new MockActivatedRoute({'debug': 'txrx'});
|
||||
|
||||
windowMock = <any>{
|
||||
location: <any> {
|
||||
hostname: 'foo',
|
||||
host: 'foo',
|
||||
port: '80',
|
||||
protocol: 'http',
|
||||
search: { debug: 'true'},
|
||||
href: 'ws://foo:123/onos/ui/websock/path',
|
||||
absUrl: 'ws://foo:123/onos/ui/websock/path'
|
||||
}
|
||||
};
|
||||
fs = new FnService(ar, logSpy, windowMock);
|
||||
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
declarations: [ DeviceComponent ],
|
||||
declarations: [ DeviceComponent, IconComponent ],
|
||||
providers: [
|
||||
{ provide: DetailsPanelService, useClass: MockDetailsPanelService },
|
||||
{ provide: FnService, useClass: MockFnService },
|
||||
{ provide: FnService, useValue: fs },
|
||||
{ provide: IconService, useClass: MockIconService },
|
||||
{ provide: GlyphService, useClass: MockGlyphService },
|
||||
{ provide: KeyService, useClass: MockKeyService },
|
||||
{ provide: LoadingService, useClass: MockLoadingService },
|
||||
{ provide: MastService, useClass: MockMastService },
|
||||
{ provide: NavService, useClass: MockNavService },
|
||||
{ provide: LogService, useValue: log },
|
||||
{ provide: LogService, useValue: logSpy },
|
||||
{ provide: PanelService, useClass: MockPanelService },
|
||||
{ provide: TableBuilderService, useClass: MockTableBuilderService },
|
||||
{ provide: TableDetailService, useClass: MockTableDetailService },
|
||||
{ provide: ThemeService, useClass: MockThemeService },
|
||||
{ provide: WebSocketService, useClass: MockWebSocketService },
|
||||
{ provide: Window, useValue: windowMock },
|
||||
]
|
||||
})
|
||||
.compileComponents();
|
||||
logServiceSpy = TestBed.get(LogService);
|
||||
}));
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(DeviceComponent);
|
||||
component = fixture.componentInstance;
|
||||
component = fixture.debugElement.componentInstance;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('should create', () => {
|
||||
expect(component).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should have .table-header with "Friendly Name..."', () => {
|
||||
const appDe: DebugElement = fixture.debugElement;
|
||||
const divDe = appDe.query(By.css('.table-header'));
|
||||
const div: HTMLElement = divDe.nativeElement;
|
||||
expect(div.textContent).toEqual('Friendly Name Device ID Master Ports Vendor H/W Version S/W Version Protocol ');
|
||||
});
|
||||
});
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user