Add link calculation app
Add link calculation app to odlux
Issue-ID: CCSDK-2562
Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com>
Change-Id: Ifc0a5b2a8bb974dfd85d70a9f05990b1f11925a3
Signed-off-by: Aijana Schumann <aijana.schumann@highstreet-technologies.com>
diff --git a/sdnr/wt/odlux/framework/pom.xml b/sdnr/wt/odlux/framework/pom.xml
index db42ef1..a7c81eb 100644
--- a/sdnr/wt/odlux/framework/pom.xml
+++ b/sdnr/wt/odlux/framework/pom.xml
@@ -46,7 +46,7 @@
<properties>
<buildtime>${maven.build.timestamp}</buildtime>
<distversion>ONAP Frankfurt (Neon, mdsal ${odl.mdsal.version})</distversion>
- <buildno>57.3e1d5cf(20/08/11)</buildno>
+ <buildno>62.ad364be(20/08/21)</buildno>
<odlux.version>ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version}</odlux.version>
</properties>
diff --git a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx
index 3dfbe0b..c5be819 100644
--- a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx
@@ -234,7 +234,7 @@
}}
role="checkbox"
aria-checked={isSelected}
- aria-label={`${(this.props.tableId ? this.props.tableId : 'table')}-row-${(index + 1)}`}
+ aria-label={`${(this.props.tableId ? this.props.tableId : 'table')}-row`}
tabIndex={-1}
key={entryId}
selected={isSelected}
@@ -284,10 +284,10 @@
rowsPerPage={rowsPerPage}
page={page}
backIconButtonProps={{
- 'aria-label': 'Previous Page',
+ 'aria-label': 'previous-page',
}}
nextIconButtonProps={{
- 'aria-label': 'Next Page',
+ 'aria-label': 'next-page',
}}
onChangePage={this.onHandleChangePage}
onChangeRowsPerPage={this.onHandleChangeRowsPerPage}
diff --git a/sdnr/wt/odlux/framework/src/components/material-table/tableFilter.tsx b/sdnr/wt/odlux/framework/src/components/material-table/tableFilter.tsx
index 464350a..76f778e 100644
--- a/sdnr/wt/odlux/framework/src/components/material-table/tableFilter.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-table/tableFilter.tsx
@@ -74,11 +74,11 @@
? null
: (col.type === ColumnType.boolean)
? <Select className={classes.input} aria-label={col.title ? (col.title as string).toLowerCase() + ' filter' : `${ind + 1}-filter`} value={filter[col.property] !== undefined ? filter[col.property] : ''} onChange={this.createFilterHandler(col.property)} inputProps={{ name: `${col.property}-bool`, id: `${col.property}-bool` }} >
- <MenuItem value={undefined} >
+ <MenuItem value={undefined} aria-label="none-value" >
<em>None</em>
</MenuItem>
- <MenuItem value={true as any as string}>{col.labels ? col.labels["true"] : "true"}</MenuItem>
- <MenuItem value={false as any as string}>{col.labels ? col.labels["false"] : "false"}</MenuItem>
+ <MenuItem aria-label="true-value" value={true as any as string}>{col.labels ? col.labels["true"] : "true"}</MenuItem>
+ <MenuItem aria-label="false-value" value={false as any as string}>{col.labels ? col.labels["false"] : "false"}</MenuItem>
</Select>
: <Input className={classes.input} inputProps={{ 'aria-label': col.title ? (col.title as string).toLowerCase() + ' filter' : `${ind + 1}-filter` }} value={filter[col.property] || ''} onChange={this.createFilterHandler(col.property)} />}
</TableCell>
diff --git a/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx b/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx
index 1a7e58f..23bad66 100644
--- a/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx
+++ b/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx
@@ -1,20 +1,20 @@
-/**
- * ============LICENSE_START========================================================================
- * ONAP : ccsdk feature sdnr wt odlux
- * =================================================================================================
- * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
- * =================================================================================================
- * 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.
- * ============LICENSE_END==========================================================================
- */
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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.
+ * ============LICENSE_END==========================================================================
+ */
import * as React from 'react';
import { NavLink, Link, Route } from 'react-router-dom';
@@ -50,7 +50,7 @@
: null
}
{ typeof Primary === 'string'
- ? <ListItemText primary={ Primary } style={{ padding: 0 }} />
+ ? <ListItemText aria-label={"link-to-"+Primary.toLowerCase()} primary={ Primary } style={{ padding: 0 }} />
: <Primary />
}
</ListItem>
diff --git a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx
index 620abd7..790677a 100644
--- a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx
+++ b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx
@@ -120,6 +120,11 @@
}
})
+ React.useEffect(()=>{
+ // trigger a resize if menu changed in case elements have to re-arrange
+ window.dispatchEvent(new Event('menu-resized'));
+ }, [isOpen])
+
return (
<Drawer
variant="permanent"
diff --git a/sdnr/wt/odlux/framework/src/components/titleBar.tsx b/sdnr/wt/odlux/framework/src/components/titleBar.tsx
index 7168ff4..49c0966 100644
--- a/sdnr/wt/odlux/framework/src/components/titleBar.tsx
+++ b/sdnr/wt/odlux/framework/src/components/titleBar.tsx
@@ -101,8 +101,8 @@
// create notificationInfo element
const notificationInfo = state.framework.applicationState.isWebsocketAvailable != undefined ?
(state.framework.applicationState.isWebsocketAvailable ?
- <Typography variant="body1" className={classes.notificationInfo}>Notifications <FontAwesomeIcon className={classes.connected} icon={faDotCircle} /> |</Typography> : <Typography variant="body1" className={classes.notificationInfo}>Notifications <FontAwesomeIcon className={classes.notConnected} icon={faBan} /> |</Typography>)
- : <Typography variant="body1" className={classes.notificationInfo}>Notifications N/A |</Typography>;
+ <Typography aria-label="notifications-are-active" variant="body1" className={classes.notificationInfo}>Notifications <FontAwesomeIcon className={classes.connected} icon={faDotCircle} /> |</Typography> : <Typography aria-label="notifications-are-inactive" variant="body1" className={classes.notificationInfo}>Notifications <FontAwesomeIcon className={classes.notConnected} icon={faBan} /> |</Typography>)
+ : <Typography variant="body1" aria-label="notifications-are-not-available" className={classes.notificationInfo}>Notifications N/A |</Typography>;
// add notificationInfo element before help
diff --git a/sdnr/wt/odlux/framework/src/services/notificationService.ts b/sdnr/wt/odlux/framework/src/services/notificationService.ts
index c90da09..4bcc05c 100644
--- a/sdnr/wt/odlux/framework/src/services/notificationService.ts
+++ b/sdnr/wt/odlux/framework/src/services/notificationService.ts
@@ -1,193 +1,193 @@
-/**
- * ============LICENSE_START========================================================================
- * ONAP : ccsdk feature sdnr wt odlux
- * =================================================================================================
- * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
- * =================================================================================================
- * 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.
- * ============LICENSE_END==========================================================================
- */
-import * as X2JS from 'x2js';
-import { ApplicationStore } from '../store/applicationStore';
-import { SetWebsocketAction } from '../actions/websocketAction';
-
-const socketUrl = [location.protocol === 'https:' ? 'wss://' : 'ws://', 'admin', ':', 'admin', '@', location.hostname, ':', location.port, '/websocket'].join('');
-const subscriptions: { [scope: string]: SubscriptionCallback[] } = {};
-let socketReady: Promise<WebSocket>;
-let userLoggedOut = false;
-let wasWebsocketConnectionEstablished: undefined | boolean;
-let applicationStore: ApplicationStore | null;
-
-
-export interface IFormatedMessage {
- notifType: string | null;
- time: string;
-}
-
-export type SubscriptionCallback<TMessage extends IFormatedMessage = IFormatedMessage> = (msg: TMessage) => void;
-
-function formatData(event: MessageEvent): IFormatedMessage | undefined {
-
- var x2js = new X2JS();
- var jsonObj: { [key: string]: IFormatedMessage } = x2js.xml2js(event.data);
- if (jsonObj && typeof (jsonObj) === 'object') {
-
- const notifType = Object.keys(jsonObj)[0];
- const formated = jsonObj[notifType];
- formated.notifType = notifType;
- formated.time = new Date().toISOString();
- return formated;
- }
- return undefined;
-
-}
-
-export function subscribe<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>): boolean {
- const scopes = scope instanceof Array ? scope : [scope];
-
- // send all new scopes to subscribe
- const newScopesToSubscribe: string[] = scopes.reduce((acc: string[], cur: string) => {
- const currentCallbacks = subscriptions[cur];
- if (currentCallbacks) {
- if (!currentCallbacks.some(c => c === callback)) {
- currentCallbacks.push(callback);
- }
- } else {
- subscriptions[cur] = [callback];
- acc.push(cur);
- }
- return acc;
- }, []);
-
- if (newScopesToSubscribe.length === 0) {
- return true;
- }
-
- return true;
-}
-
-
-export function unsubscribe<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>): Promise<boolean> {
- return socketReady.then((notificationSocket) => {
- const scopes = scope instanceof Array ? scope : [scope];
- scopes.forEach(s => {
- const callbacks = subscriptions[s];
- const index = callbacks && callbacks.indexOf(callback);
- if (index > -1) {
- callbacks.splice(index, 1);
- }
- if (callbacks.length === 0) {
- subscriptions[s] === undefined;
- }
- });
-
- // send a subscription to all active scopes
- const scopesToSubscribe = Object.keys(subscriptions);
- if (notificationSocket.readyState === notificationSocket.OPEN) {
- const data = {
- 'data': 'scopes',
- 'scopes': scopesToSubscribe
- };
- notificationSocket.send(JSON.stringify(data));
- return true;
- }
- return false;
- });
-}
-
-export const startNotificationService = (store: ApplicationStore) => {
- applicationStore = store;
-}
-
-const connect = (): Promise<WebSocket> => {
- return new Promise((resolve, reject) => {
- const notificationSocket = new WebSocket(socketUrl);
-
- notificationSocket.onmessage = (event) => {
- // process received event
- if (typeof event.data === 'string') {
- const formated = formatData(event);
- if (formated && formated.notifType) {
- const callbacks = subscriptions[formated.notifType];
- if (callbacks) {
- callbacks.forEach(cb => {
- // ensure all callbacks will be called
- try {
- return cb(formated);
- } catch (reason) {
- console.error(reason);
- }
- });
- }
- }
- }
- };
-
- notificationSocket.onerror = function (error) {
- console.log("Socket error:");
- console.log(error);
- reject("Socket error: " + error);
- if (applicationStore) {
- applicationStore.dispatch(new SetWebsocketAction(false));
- }
- };
-
- notificationSocket.onopen = function (event) {
- if (applicationStore) {
- applicationStore.dispatch(new SetWebsocketAction(true));
- }
- console.log("Socket connection opened.");
- resolve(notificationSocket);
-
- // send a subscription to all active scopes
- const scopesToSubscribe = Object.keys(subscriptions);
- if (notificationSocket.readyState === notificationSocket.OPEN) {
- const data = {
- 'data': 'scopes',
- 'scopes': scopesToSubscribe
- };
- notificationSocket.send(JSON.stringify(data));
- };
- };
-
- notificationSocket.onclose = function (event) {
- console.log("socket connection closed");
- if (applicationStore) {
- applicationStore.dispatch(new SetWebsocketAction(false));
- }
- if (!userLoggedOut) {
- socketReady = connect();
- }
- };
- });
-}
-
-
-
-
-export const startWebsocketSession = () => {
- socketReady = connect();
- userLoggedOut = false;
-}
-
-export const endWebsocketSession = () => {
- if (socketReady) {
- socketReady.then(websocket => {
- websocket.close();
- userLoggedOut = true;
- });
- }
-
-}
-
-
-
-
+/**
+ * ============LICENSE_START========================================================================
+ * ONAP : ccsdk feature sdnr wt odlux
+ * =================================================================================================
+ * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved.
+ * =================================================================================================
+ * 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.
+ * ============LICENSE_END==========================================================================
+ */
+import * as X2JS from 'x2js';
+import { ApplicationStore } from '../store/applicationStore';
+import { SetWebsocketAction } from '../actions/websocketAction';
+
+const socketUrl = [location.protocol === 'https:' ? 'wss://' : 'ws://', 'admin', ':', 'admin', '@', location.hostname, ':', location.port, '/websocket'].join('');
+const subscriptions: { [scope: string]: SubscriptionCallback[] } = {};
+let socketReady: Promise<WebSocket>;
+let userLoggedOut = false;
+let wasWebsocketConnectionEstablished: undefined | boolean;
+let applicationStore: ApplicationStore | null;
+
+
+export interface IFormatedMessage {
+ notifType: string | null;
+ time: string;
+}
+
+export type SubscriptionCallback<TMessage extends IFormatedMessage = IFormatedMessage> = (msg: TMessage) => void;
+
+function formatData(event: MessageEvent): IFormatedMessage | undefined {
+
+ var x2js = new X2JS();
+ var jsonObj: { [key: string]: IFormatedMessage } = x2js.xml2js(event.data);
+ if (jsonObj && typeof (jsonObj) === 'object') {
+
+ const notifType = Object.keys(jsonObj)[0];
+ const formated = jsonObj[notifType];
+ formated.notifType = notifType;
+ formated.time = new Date().toISOString();
+ return formated;
+ }
+ return undefined;
+
+}
+
+export function subscribe<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>): boolean {
+ const scopes = scope instanceof Array ? scope : [scope];
+
+ // send all new scopes to subscribe
+ const newScopesToSubscribe: string[] = scopes.reduce((acc: string[], cur: string) => {
+ const currentCallbacks = subscriptions[cur];
+ if (currentCallbacks) {
+ if (!currentCallbacks.some(c => c === callback)) {
+ currentCallbacks.push(callback);
+ }
+ } else {
+ subscriptions[cur] = [callback];
+ acc.push(cur);
+ }
+ return acc;
+ }, []);
+
+ if (newScopesToSubscribe.length === 0) {
+ return true;
+ }
+
+ return true;
+}
+
+
+export function unsubscribe<TMessage extends IFormatedMessage = IFormatedMessage>(scope: string | string[], callback: SubscriptionCallback<TMessage>): Promise<boolean> {
+ return socketReady.then((notificationSocket) => {
+ const scopes = scope instanceof Array ? scope : [scope];
+ scopes.forEach(s => {
+ const callbacks = subscriptions[s];
+ const index = callbacks && callbacks.indexOf(callback);
+ if (index > -1) {
+ callbacks.splice(index, 1);
+ }
+ if (callbacks.length === 0) {
+ subscriptions[s] === undefined;
+ }
+ });
+
+ // send a subscription to all active scopes
+ const scopesToSubscribe = Object.keys(subscriptions);
+ if (notificationSocket.readyState === notificationSocket.OPEN) {
+ const data = {
+ 'data': 'scopes',
+ 'scopes': scopesToSubscribe
+ };
+ notificationSocket.send(JSON.stringify(data));
+ return true;
+ }
+ return false;
+ });
+}
+
+export const startNotificationService = (store: ApplicationStore) => {
+ applicationStore = store;
+}
+
+const connect = (): Promise<WebSocket> => {
+ return new Promise((resolve, reject) => {
+ const notificationSocket = new WebSocket(socketUrl);
+
+ notificationSocket.onmessage = (event) => {
+ // process received event
+ if (typeof event.data === 'string') {
+ const formated = formatData(event);
+ if (formated && formated.notifType) {
+ const callbacks = subscriptions[formated.notifType];
+ if (callbacks) {
+ callbacks.forEach(cb => {
+ // ensure all callbacks will be called
+ try {
+ return cb(formated);
+ } catch (reason) {
+ console.error(reason);
+ }
+ });
+ }
+ }
+ }
+ };
+
+ notificationSocket.onerror = function (error) {
+ console.log("Socket error:");
+ console.log(error);
+ reject("Socket error: " + error);
+ if (applicationStore) {
+ applicationStore.dispatch(new SetWebsocketAction(false));
+ }
+ };
+
+ notificationSocket.onopen = function (event) {
+ if (applicationStore) {
+ applicationStore.dispatch(new SetWebsocketAction(true));
+ }
+ console.log("Socket connection opened.");
+ resolve(notificationSocket);
+
+ // send a subscription to all active scopes
+ const scopesToSubscribe = Object.keys(subscriptions);
+ if (notificationSocket.readyState === notificationSocket.OPEN) {
+ const data = {
+ 'data': 'scopes',
+ 'scopes': scopesToSubscribe
+ };
+ notificationSocket.send(JSON.stringify(data));
+ };
+ };
+
+ notificationSocket.onclose = function (event) {
+ console.log("socket connection closed");
+ if (applicationStore) {
+ applicationStore.dispatch(new SetWebsocketAction(false));
+ }
+ if (!userLoggedOut) {
+ socketReady = connect();
+ }
+ };
+ });
+}
+
+
+
+
+export const startWebsocketSession = () => {
+ socketReady = connect();
+ userLoggedOut = false;
+}
+
+export const endWebsocketSession = () => {
+ if (socketReady) {
+ socketReady.then(websocket => {
+ websocket.close();
+ userLoggedOut = true;
+ });
+ }
+
+}
+
+
+
+
diff --git a/sdnr/wt/odlux/framework/src/views/about.tsx b/sdnr/wt/odlux/framework/src/views/about.tsx
index c4a5488..f97d6ff 100644
--- a/sdnr/wt/odlux/framework/src/views/about.tsx
+++ b/sdnr/wt/odlux/framework/src/views/about.tsx
@@ -40,11 +40,23 @@
this.textarea = React.createRef();
this.loadAboutContent();
}
+ private getMarkOdluxVersionMarkdownTable(data:{version:string,build:string}|null|undefined):string{
+ if(!data) {
+ return "";
+ }
+ return `| | |\n| --- | --- |\n| Version | ${data.version} |\n| Build timestamp | ${data.build}|`
+ }
private loadAboutContent(): void {
- requestRestExt<string>('/about').then((response) => {
+ const baseUri = window.location.pathname.substring(0,window.location.pathname.lastIndexOf("/")+1);
+ const p1 = requestRestExt<string>('/about');
+ const p2 = requestRestExt<{version:string,build:string}>(`${baseUri}version.json`);
+ Promise.all([p1,p2]).then((responses) => {
+ const response = responses[0];
+ const response2 = responses[1];
const content = response.status == 200 ? response.data : `${response.status} ${response.message}` || "Server error";
+ const content2 = `\n## ODLUX Version Info\n`+(response2.status == 200 ? this.getMarkOdluxVersionMarkdownTable(response2.data) : `${response2.status} ${response2.message}` || "ODLUX Server error");
const loadedSucessfully = response.status == 200 ? true : false;
- this.setState({ content: content || null, isContentLoadedSucessfully: loadedSucessfully });
+ this.setState({ content: (content + content2) || null, isContentLoadedSucessfully: loadedSucessfully });
}).catch((error) => {
this.setState({ content: error })
})
diff --git a/sdnr/wt/odlux/framework/src/views/login.tsx b/sdnr/wt/odlux/framework/src/views/login.tsx
index 30b9c85..b06cf76 100644
--- a/sdnr/wt/odlux/framework/src/views/login.tsx
+++ b/sdnr/wt/odlux/framework/src/views/login.tsx
@@ -142,6 +142,7 @@
label="Remember me"
/>
<Button
+ aria-label="login-button"
type="submit"
fullWidth
variant="contained"
diff --git a/sdnr/wt/odlux/framework/webpack.config.js b/sdnr/wt/odlux/framework/webpack.config.js
index c7ef72e..ad5b4cc 100644
--- a/sdnr/wt/odlux/framework/webpack.config.js
+++ b/sdnr/wt/odlux/framework/webpack.config.js
@@ -171,7 +171,11 @@
new webpack.WatchIgnorePlugin([
/css\.d\.ts$/,
/less\.d\.ts$/
- ])
+ ]),
+ new CopyWebpackPlugin([{
+ from: './assets/version.json',
+ to: './version.json'
+ }])
]
],
@@ -210,6 +214,10 @@
target: "http://10.20.6.29:48181",
secure: false
},
+ "/about": {
+ target: "http://10.20.6.29:48181",
+ secure: false
+ },
"/websocket": {
target: "http://10.20.6.29:48181",
ws: true,