diff options
Diffstat (limited to 'sdnr/wt-odlux/odlux/apps/mediatorApp')
23 files changed, 2661 insertions, 0 deletions
diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/.babelrc b/sdnr/wt-odlux/odlux/apps/mediatorApp/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/.babelrc @@ -0,0 +1,17 @@ +{ + "presets": [ + ["@babel/preset-react"], + ["@babel/preset-env", { + "targets": { + "chrome": "66" + }, + "spec": true, + "loose": false, + "modules": false, + "debug": false, + "useBuiltIns": "usage", + "forceAllTransforms": true + }] + ], + "plugins": [] +} diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/package.json b/sdnr/wt-odlux/odlux/apps/mediatorApp/package.json new file mode 100644 index 000000000..867a8790d --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/package.json @@ -0,0 +1,44 @@ +{ + "name": "@odlux/mediator-app", + "version": "0.1.0", + "description": "A react based modular UI to demo the mediator possible app.", + "main": "index.js", + "scripts": { + "start": "webpack-dev-server --env debug", + "build": "webpack --env release --config webpack.config.js", + "build:dev": "webpack --env debug --config webpack.config.js" + }, + "repository": { + "type": "git", + "url": "https://git.mfico.de/highstreet-technologies/odlux.git" + }, + "keywords": [ + "reactjs", + "redux", + "ui", + "framework" + ], + "author": "Matthias Fischer", + "license": "Apache-2.0", + "dependencies": { + "@emotion/react": "^11.7.0", + "@emotion/styled": "^11.6.0", + "@mui/icons-material": "^5.2.0", + "@mui/material": "^5.2.2", + "@mui/styles": "^5.2.2", + "@odlux/framework": "*" + }, + "peerDependencies": { + "@fortawesome/free-solid-svg-icons": "5.6.3", + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "@types/react": "17.0.37", + "@types/react-dom": "17.0.11", + "@types/react-router-dom": "5.1.7", + "jquery": "3.3.1", + "react": "17.0.2", + "react-dom": "17.0.2", + "react-router-dom": "5.2.0" + } +} diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/pom.xml b/sdnr/wt-odlux/odlux/apps/mediatorApp/pom.xml new file mode 100644 index 000000000..547c91e92 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/pom.xml @@ -0,0 +1,105 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + ~ ============LICENSE_START======================================================= + ~ ONAP : SDNR ODLUX + ~ ================================================================================ + ~ Copyright (C) 2020 AT&T 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======================================================= + ~ + --> + +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <modelVersion>4.0.0</modelVersion> + + <groupId>org.onap.ccsdk.features.sdnr.odlux</groupId> + <artifactId>sdnr-odlux-app-mediatorApp</artifactId> + <version>1.7.0-SNAPSHOT</version> + <packaging>jar</packaging> + + <name>SDNR ODLUX :: ${project.artifactId}</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + + <build> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + </resources> + <plugins> + <plugin> + <artifactId>maven-clean-plugin</artifactId> + <configuration> + <filesets> + <fileset> + <directory>dist</directory> + <followSymlinks>false</followSymlinks> + </fileset> + <fileset> + <directory>node</directory> + <followSymlinks>false</followSymlinks> + </fileset> + <fileset> + <directory>node_modules</directory> + <followSymlinks>false</followSymlinks> + </fileset> + <fileset> + <directory>../node_modules</directory> + <followSymlinks>false</followSymlinks> + </fileset> + <!-- eclipse bug build bin folder in basedir --> + <fileset> + <directory>bin</directory> + <followSymlinks>false</followSymlinks> + </fileset> + </filesets> + </configuration> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.2</version> + <executions> + <execution> + <id>install node and yarn</id> + <goals> + <goal>install-node-and-yarn</goal> + </goals> + <!-- optional: default phase is "generate-resources" --> + <phase>initialize</phase> + <configuration> + <nodeVersion>v16.17.0</nodeVersion> + <yarnVersion>v1.22.19</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/actions/avaliableMediatorServersActions.ts b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/actions/avaliableMediatorServersActions.ts new file mode 100644 index 000000000..3f56b05e1 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/actions/avaliableMediatorServersActions.ts @@ -0,0 +1,58 @@ +/** + * ============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 { Action } from '../../../../framework/src/flux/action'; +import { Dispatch } from '../../../../framework/src/flux/store'; +import { AddSnackbarNotification } from '../../../../framework/src/actions/snackbarActions'; + +import { MediatorServer } from '../models/mediatorServer'; +import { avaliableMediatorServersReloadAction } from '../handlers/avaliableMediatorServersHandler'; +import mediatorService from '../services/mediatorService'; + +/** Represents the base action. */ +export class BaseAction extends Action { } + +/** Represents an async thunk action that will add a server to the avaliable mediator servers. */ +export const addAvaliableMediatorServerAsyncActionCreator = (server: MediatorServer) => (dispatch: Dispatch) => { + mediatorService.insertMediatorServer(server).then(_ => { + window.setTimeout(() => { + dispatch(avaliableMediatorServersReloadAction); + dispatch(new AddSnackbarNotification({ message: `Successfully added [${ server.name }]`, options: { variant: 'success' } })); + }, 900); + }); + }; + + /** Represents an async thunk action that will add a server to the avaliable mediator servers. */ +export const updateAvaliableMediatorServerAsyncActionCreator = (server: MediatorServer) => (dispatch: Dispatch) => { + mediatorService.updateMediatorServer(server).then(_ => { + window.setTimeout(() => { + dispatch(avaliableMediatorServersReloadAction); + dispatch(new AddSnackbarNotification({ message: `Successfully updated [${ server.name }]`, options: { variant: 'success' } })); + }, 900); + }); +}; + + /** Represents an async thunk action that will delete a server from the avaliable mediator servers. */ + export const removeAvaliableMediatorServerAsyncActionCreator = (server: MediatorServer) => (dispatch: Dispatch) => { + mediatorService.deleteMediatorServer(server).then(_ => { + window.setTimeout(() => { + dispatch(avaliableMediatorServersReloadAction); + dispatch(new AddSnackbarNotification({ message: `Successfully removed [${ server.name }]`, options: { variant: 'success' } })); + }, 900); + }); + }; +
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/actions/mediatorConfigActions.ts b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/actions/mediatorConfigActions.ts new file mode 100644 index 000000000..516515ab2 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/actions/mediatorConfigActions.ts @@ -0,0 +1,154 @@ +/** + * ============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 { Action } from '../../../../framework/src/flux/action'; +import { Dispatch } from '../../../../framework/src/flux/store'; + +import { AddSnackbarNotification } from '../../../../framework/src/actions/snackbarActions'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import mediatorService from '../services/mediatorService'; +import { MediatorConfig, MediatorConfigResponse } from '../models/mediatorServer'; + +/** Represents the base action. */ +export class BaseAction extends Action { } + +export class SetMediatorBusyByName extends BaseAction { + constructor(public name: string, public isBusy: boolean) { + super(); + } +} + +export class AddMediatorConfig extends BaseAction { + constructor(public mediatorConfig: MediatorConfigResponse) { + super(); + } +} + +export class UpdateMediatorConfig extends BaseAction { + constructor(public name: string, public mediatorConfig: MediatorConfigResponse) { + super(); + } +} + +export class RemoveMediatorConfig extends BaseAction { + constructor(public name: string) { + super(); + } +} + + +export const startMediatorByNameAsyncActionCreator = (name: string) => (dispatch: Dispatch, getState: () => IApplicationStoreState) => { + dispatch(new SetMediatorBusyByName(name, true)); + const { mediator: { mediatorServerState: { id } } } = getState(); + if (id) { + mediatorService.startMediatorByName(id, name).then(msg => { + dispatch(new AddSnackbarNotification({ message: msg + ' ' + name, options: { variant: 'info' } })); + // since there is no notification, a timeout will be need here + window.setTimeout(() => { + mediatorService.getMediatorServerConfigByName(id, name).then(config => { + if (config) { + dispatch(new UpdateMediatorConfig(name, config)); + } else { + dispatch(new AddSnackbarNotification({ message: `Error: reading mediator config for ${name}.`, options: { variant: 'error' } })); + } + dispatch(new SetMediatorBusyByName(name, false)); + }); + }, 2100); + }); + } else { + dispatch(new AddSnackbarNotification({ message: `Error: currently no mediator server selected.`, options: { variant: 'error' } })); + dispatch(new SetMediatorBusyByName(name, false)); + } +}; + +export const stopMediatorByNameAsyncActionCreator = (name: string) => (dispatch: Dispatch, getState: () => IApplicationStoreState) => { + dispatch(new SetMediatorBusyByName(name, true)); + const { mediator: { mediatorServerState: { id } } } = getState(); + if (id) { + mediatorService.stopMediatorByName(id, name).then(msg => { + dispatch(new AddSnackbarNotification({ message: msg + ' ' + name, options: { variant: 'info' } })); + // since there is no notification, a timeout will be need here + window.setTimeout(() => { + mediatorService.getMediatorServerConfigByName(id, name).then(config => { + if (config) { + dispatch(new UpdateMediatorConfig(name, config)); + } else { + dispatch(new AddSnackbarNotification({ message: `Error: reading mediator config for ${name}.`, options: { variant: 'error' } })); + } + dispatch(new SetMediatorBusyByName(name, false)); + }); + }, 2100); + }); + } else { + dispatch(new AddSnackbarNotification({ message: `Error: currently no mediator server selected.`, options: { variant: 'error' } })); + dispatch(new SetMediatorBusyByName(name, false)); + } +}; + +export const addMediatorConfigAsyncActionCreator = (config: MediatorConfig) => (dispatch: Dispatch, getState: () => IApplicationStoreState) => { + const { Name: name } = config; + const { mediator: { mediatorServerState: { id } } } = getState(); + if (id) { + mediatorService.createMediatorConfig(id, config).then(msg => { + dispatch(new AddSnackbarNotification({ message: msg + ' ' + name, options: { variant: 'info' } })); + // since there is no notification, a timeout will be need here + window.setTimeout(() => { + mediatorService.getMediatorServerConfigByName(id, name).then(config => { + if (config) { + dispatch(new AddMediatorConfig(config)); + } else { + dispatch(new AddSnackbarNotification({ message: `Error: reading mediator config for ${name}.`, options: { variant: 'error' } })); + } + }); + }, 2100); + }); + } else { + dispatch(new AddSnackbarNotification({ message: `Error: currently no mediator server selected.`, options: { variant: 'error' } })); + } +}; + +export const updateMediatorConfigAsyncActionCreator = (config: MediatorConfig) => (dispatch: Dispatch) => { + // currently not supported be backend +}; + +export const removeMediatorConfigAsyncActionCreator = (config: MediatorConfig) => (dispatch: Dispatch, getState: () => IApplicationStoreState) => { + const { Name: name } = config; + const { mediator: { mediatorServerState: { id } } } = getState(); + if (id) { + mediatorService.deleteMediatorConfigByName(id, name).then(msg => { + dispatch(new AddSnackbarNotification({ message: msg + ' ' + name, options: { variant: 'info' } })); + // since there is no notification, a timeout will be need here + window.setTimeout(() => { + mediatorService.getMediatorServerConfigByName(id, config.Name).then(config => { + if (!config) { + dispatch(new RemoveMediatorConfig(name)); + } else { + dispatch(new AddSnackbarNotification({ message: `Error: deleting mediator config for ${name}.`, options: { variant: 'error' } })); + } + }); + }, 2100); + }); + } else { + dispatch(new AddSnackbarNotification({ message: `Error: currently no mediator server selected.`, options: { variant: 'error' } })); + dispatch(new SetMediatorBusyByName(name, false)); + } +}; + + + diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/actions/mediatorServerActions.ts b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/actions/mediatorServerActions.ts new file mode 100644 index 000000000..79e46df0e --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/actions/mediatorServerActions.ts @@ -0,0 +1,114 @@ +/** + * ============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 { Action } from '../../../../framework/src/flux/action'; +import { Dispatch } from '../../../../framework/src/flux/store'; + +import { MediatorServerVersionInfo, MediatorConfig, MediatorConfigResponse, MediatorServerDevice } from '../models/mediatorServer'; +import mediatorService from '../services/mediatorService'; +import { AddSnackbarNotification } from '../../../../framework/src/actions/snackbarActions'; +import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +/** Represents the base action. */ +export class BaseAction extends Action { } + +export class SetMediatorServerBusy extends BaseAction { + constructor(public isBusy: boolean) { + super(); + } +} + +export class SetMediatorServerInfo extends BaseAction { + /** + * Initializes a new instance of this class. + */ + constructor(public id: string | null, public name: string | null, public url: string | null) { + super(); + + } +} + +export class SetMediatorServerVersion extends BaseAction { + /** + * Initializes a new instance of this class. + */ + constructor(public versionInfo: MediatorServerVersionInfo | null) { + super(); + + } +} + +export class SetAllMediatorServerConfigurations extends BaseAction { + /** + * Initializes a new instance of this class. + */ + constructor(public allConfigurations: MediatorConfigResponse[] | null) { + super(); + + } +} + +export class SetMediatorServerSupportedDevices extends BaseAction { + /** + * Initializes a new instance of this class. + */ + constructor(public devices: MediatorServerDevice[] | null) { + super(); + + } +} + +export class SetMediatorServerReachable extends BaseAction { + constructor(public isReachable: boolean) { + super(); + } +} + +export const initializeMediatorServerAsyncActionCreator = (serverId: string) => (dispatch: Dispatch) => { + dispatch(new SetMediatorServerBusy(true)); + mediatorService.getMediatorServerById(serverId).then(mediatorServer => { + if (!mediatorServer) { + dispatch(new SetMediatorServerBusy(false)); + dispatch(new AddSnackbarNotification({ message: `Error loading mediator server [${serverId}]`, options: { variant: 'error' } })); + dispatch(new NavigateToApplication("mediator")); + return; + } + + dispatch(new SetMediatorServerInfo(mediatorServer.id, mediatorServer.name, mediatorServer.url)); + + Promise.all([ + mediatorService.getMediatorServerAllConfigs(mediatorServer.id), + mediatorService.getMediatorServerSupportedDevices(mediatorServer.id), + mediatorService.getMediatorServerVersion(mediatorServer.id) + ]).then(([configurations, supportedDevices, versionInfo]) => { + if (configurations === null && supportedDevices === null && versionInfo === null) { + dispatch(new SetMediatorServerReachable(false)); + } else { + dispatch(new SetMediatorServerReachable(true)); + } + dispatch(new SetAllMediatorServerConfigurations(configurations)); + dispatch(new SetMediatorServerSupportedDevices(supportedDevices)); + dispatch(new SetMediatorServerVersion(versionInfo)); + dispatch(new SetMediatorServerBusy(false)); + }).catch(error => { + dispatch(new SetMediatorServerReachable(false)); + dispatch(new SetMediatorServerBusy(false)); + }); + }); +}; + diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/assets/icons/mediatorAppIcon.svg b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/assets/icons/mediatorAppIcon.svg new file mode 100644 index 000000000..b819aa610 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/assets/icons/mediatorAppIcon.svg @@ -0,0 +1,49 @@ +<!-- highstreet technologies GmbH colour scheme + Grey #565656 + LBlue #36A9E1 + DBlue #246DA2 + Green #003F2C / #006C4B + Yellw #C8D400 + Red #D81036 +--> + +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 980 980"> + +<g transform="translate(0.000000,890.000000) scale(0.100000,-0.100000)"> + +<path fill="#36A9E1" d="M4704 7791 c-314 -180 -1992 -1183 -1993 -1191 -2 -12 2183 -1311 +2198 -1307 20 5 1595 947 1856 1109 198 123 295 193 295 210 0 7 -22 26 -48 +42 -352 217 -2060 1224 -2103 1241 -9 3 -94 -40 -205 -104z"/> + +<path fill="#36A9E1" d="M2530 5147 l0 -1144 1098 -731 1097 -732 3 1217 c2 966 -1 1220 -10 +1229 -19 17 -2169 1304 -2179 1304 -5 0 -9 -475 -9 -1143z"/> + +<path fill="#36A9E1" d="M6163 5638 l-1093 -653 0 -1232 c0 -678 2 -1233 4 -1233 8 0 2161 +1293 2179 1308 16 14 17 62 16 761 -1 410 -5 961 -8 1224 l-6 477 -1092 -652z"/> + +<path fill="#565656" d="M7598 4488 c-3 -626 -6 -800 -17 -825 -16 -39 -136 -122 -486 -337 +-132 -81 -249 -154 -260 -162 -20 -15 -19 -16 20 -50 44 -39 414 -264 517 +-315 l66 -32 109 63 c107 62 414 243 838 493 383 226 1199 715 1219 730 19 14 +16 17 -50 59 -97 62 -1941 1168 -1948 1168 -3 0 -6 -357 -8 -792z"/> + +<path fill="#565656" d="M1280 4736 c-1071 -644 -1095 -659 -1093 -671 0 -5 492 -303 1092 +-661 l1091 -653 400 197 c219 108 399 199 399 202 0 3 -102 73 -226 155 -333 +220 -653 442 -691 479 -24 24 -35 45 -42 86 -6 30 -13 358 -16 728 -5 477 -10 +672 -18 671 -6 0 -409 -240 -896 -533z"/> + +<path fill="#565656" d="M0 2612 l0 -1149 872 -578 c1051 -697 1292 -855 1307 -855 8 0 11 +333 11 1213 l0 1212 -1063 637 c-584 350 -1077 643 -1094 652 l-33 17 0 -1149z"/> + +<path fill="#565656" d="M9380 3514 c-217 -129 -704 -420 -1082 -647 l-688 -412 0 -1228 0 +-1228 28 15 c56 29 810 476 1692 1003 l465 278 3 1228 c2 978 0 1227 -10 1227 +-7 0 -191 -106 -408 -236z"/> + +<path fill="#565656" d="M3019 2685 l-484 -243 2 -1124 c2 -1073 6 -1308 22 -1308 11 0 1943 +1147 2134 1267 l37 23 0 413 0 412 -378 250 c-331 219 -807 530 -838 548 -6 3 +-229 -103 -495 -238z"/> + +<path fill="#565656" d="M6249 2821 c-101 -60 -407 -244 -679 -408 l-495 -299 0 -409 0 -410 +440 -263 c1265 -755 1717 -1022 1733 -1022 9 0 12 292 12 1218 0 669 -4 1222 +-8 1228 -13 19 -788 474 -808 474 -6 0 -93 -49 -195 -109z"/> +</g> +</svg> diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/components/editMediatorConfigDialog.tsx b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/components/editMediatorConfigDialog.tsx new file mode 100644 index 000000000..34ffc5e20 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/components/editMediatorConfigDialog.tsx @@ -0,0 +1,399 @@ +/** + * ============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 { Theme, Typography, FormControlLabel, Checkbox } from '@mui/material'; + +import { WithStyles } from '@mui/styles'; +import createStyles from '@mui/styles/createStyles'; +import withStyles from '@mui/styles/withStyles'; + +import Button from '@mui/material/Button'; +import TextField from '@mui/material/TextField'; +import Select from '@mui/material/Select'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; + +import Tabs from '@mui/material/Tabs'; +import Tab from '@mui/material/Tab'; + +import Fab from '@mui/material/Fab'; +import AddIcon from '@mui/icons-material/Add'; +import DeleteIcon from '@mui/icons-material/Delete'; +import IconButton from '@mui/material/IconButton'; + +import { addMediatorConfigAsyncActionCreator, updateMediatorConfigAsyncActionCreator, removeMediatorConfigAsyncActionCreator } from '../actions/mediatorConfigActions'; +import { MediatorConfig, ODLConfig } from '../models/mediatorServer'; +import FormControl from '@mui/material/FormControl'; +import InputLabel from '@mui/material/InputLabel'; +import MenuItem from '@mui/material/MenuItem'; + +import { Panel } from '../../../../framework/src/components/material-ui/panel'; + +import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + + +const styles = (theme: Theme) => createStyles({ + root: { + display: 'flex', + flexDirection: 'column', + flex: '1', + }, + fab: { + position: 'absolute', + bottom: theme.spacing(1), + right: theme.spacing(1), + }, + title: { + fontSize: 14, + }, + center: { + flex: "1", + display: "flex", + alignItems: "center", + justifyContent: "center", + }, + alignInOneLine: { + display: 'flex', + flexDirection: 'row' + }, + left: { + marginRight: theme.spacing(1), + }, + right: { + marginLeft: 0, + } +}); + +const TabContainer: React.SFC = ({ children }) => { + return ( + <div style={{ width: "430px", height: "530px", position: "relative", display: 'flex', flexDirection: 'column' }}> + {children} + </div> + ); +} + +export enum EditMediatorConfigDialogMode { + None = "none", + AddMediatorConfig = "addMediatorConfig", + EditMediatorConfig = "editMediatorConfig", + RemoveMediatorConfig = "removeMediatorConfig", +} + +const mapProps = (state: IApplicationStoreState) => ({ + supportedDevices: state.mediator.mediatorServerState.supportedDevices +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + addMediatorConfig: (config: MediatorConfig) => { + dispatcher.dispatch(addMediatorConfigAsyncActionCreator(config)); + }, + updateMediatorConfig: (config: MediatorConfig) => { + dispatcher.dispatch(updateMediatorConfigAsyncActionCreator(config)); + }, + removeMediatorConfig: (config: MediatorConfig) => { + dispatcher.dispatch(removeMediatorConfigAsyncActionCreator(config)); + }, +}); + +type DialogSettings = { + dialogTitle: string; + dialogDescription: string; + applyButtonText: string; + cancelButtonText: string; + readonly: boolean; + readonlyName: boolean; +}; + +const settings: { [key: string]: DialogSettings } = { + [EditMediatorConfigDialogMode.None]: { + dialogTitle: "", + dialogDescription: "", + applyButtonText: "", + cancelButtonText: "", + readonly: true, + readonlyName: true, + }, + [EditMediatorConfigDialogMode.AddMediatorConfig]: { + dialogTitle: "Add Mediator Configuration", + dialogDescription: "", + applyButtonText: "Add", + cancelButtonText: "Cancel", + readonly: false, + readonlyName: false, + }, + [EditMediatorConfigDialogMode.EditMediatorConfig]: { + dialogTitle: "Edit Mediator Configuration", + dialogDescription: "", + applyButtonText: "Update", + cancelButtonText: "Cancel", + readonly: false, + readonlyName: true, + }, + [EditMediatorConfigDialogMode.RemoveMediatorConfig]: { + dialogTitle: "Remove Mediator Configuration", + dialogDescription: "", + applyButtonText: "Remove", + cancelButtonText: "Cancel", + readonly: true, + readonlyName: true, + }, +}; + +type EditMediatorConfigDialogComponentProps = WithStyles<typeof styles> & Connect<typeof mapProps, typeof mapDispatch> & { + mode: EditMediatorConfigDialogMode; + mediatorConfig: MediatorConfig; + onClose: () => void; +}; + +type EditMediatorConfigDialogComponentState = MediatorConfig & { activeTab: number; activeOdlConfig: string, forceAddOdlConfig: boolean, isOdlConfigHostnameEmpty: boolean }; + +class EditMediatorConfigDialogComponent extends React.Component<EditMediatorConfigDialogComponentProps, EditMediatorConfigDialogComponentState> { + constructor(props: EditMediatorConfigDialogComponentProps) { + super(props); + + this.state = { + ...this.props.mediatorConfig, + activeTab: 0, + activeOdlConfig: "", + forceAddOdlConfig: false, + isOdlConfigHostnameEmpty: false + }; + } + + private odlConfigValueChangeHandlerCreator = <THtmlElement extends HTMLElement, TKey extends keyof ODLConfig>(index: number, property: TKey, mapValue: (event: React.ChangeEvent<THtmlElement>) => any) => (event: React.ChangeEvent<THtmlElement>) => { + event.stopPropagation(); + event.preventDefault(); + this.setState({ + ODLConfig: [ + ...this.state.ODLConfig.slice(0, index), + { ...this.state.ODLConfig[index], [property]: mapValue(event) }, + ...this.state.ODLConfig.slice(index + 1) + ] + }); + } + + render(): JSX.Element { + const setting = settings[this.props.mode]; + const { classes } = this.props; + return ( + <Dialog open={this.props.mode !== EditMediatorConfigDialogMode.None}> + <DialogTitle id="form-dialog-title">{setting.dialogTitle}</DialogTitle> + <DialogContent> + <DialogContentText> + {setting.dialogDescription} + </DialogContentText> + <Tabs value={this.state.activeTab} indicatorColor="secondary" textColor="secondary" onChange={(event, value) => this.setState({ activeTab: value })} > + <Tab label="Config" /> + <Tab label="ODL AutoConnect" /> + </Tabs> + {this.state.activeTab === 0 ? <TabContainer > + <TextField variant="standard" disabled={setting.readonly || setting.readonlyName} spellCheck={false} autoFocus margin="dense" id="name" label="Name" type="text" fullWidth value={this.state.Name} onChange={(event) => { this.setState({ Name: event.target.value }); }} /> + <FormControl variant="standard" fullWidth disabled={setting.readonly}> + <InputLabel htmlFor="deviceType">Device</InputLabel> + <Select variant="standard" value={this.state.DeviceType} onChange={(event, value) => { + const device = this.props.supportedDevices.find(device => device.id === event.target.value); + if (device) { + this.setState({ + DeviceType: device.id, + NeXMLFile: device.xml + }); + } else { + this.setState({ + DeviceType: -1, + NeXMLFile: "" + }); + } + }} inputProps={{ name: 'deviceType', id: 'deviceType' }} fullWidth > + <MenuItem value={-1}><em>None</em></MenuItem> + {this.props.supportedDevices.map(device => (<MenuItem key={device.id} value={device.id} >{`${device.vendor} - ${device.device} (${device.version || '0.0.0'}) `}</MenuItem>))} + </Select> + </FormControl> + <TextField variant="standard" disabled={setting.readonly} spellCheck={false} autoFocus margin="dense" id="ipAddress" label="Device IP" type="text" fullWidth value={this.state.DeviceIp} onChange={(event) => { this.setState({ DeviceIp: event.target.value }); }} /> + <TextField variant="standard" disabled={setting.readonly} spellCheck={false} autoFocus margin="dense" id="devicePort" label="Device SNMP Port" type="number" fullWidth value={this.state.DevicePort || ""} onChange={(event) => { this.setState({ DevicePort: +event.target.value }); }} /> + <TextField variant="standard" disabled={setting.readonly} spellCheck={false} autoFocus margin="dense" id="trapsPort" label="TrapsPort" type="number" fullWidth value={this.state.TrapPort || ""} onChange={(event) => { this.setState({ TrapPort: +event.target.value }); }} /> + <TextField variant="standard" disabled={setting.readonly} spellCheck={false} autoFocus margin="dense" id="ncUser" label="Netconf User" type="text" fullWidth value={this.state.NcUsername} onChange={(event) => { this.setState({ NcUsername: event.target.value }); }} /> + <TextField variant="standard" disabled={setting.readonly} spellCheck={false} autoFocus margin="dense" id="ncPassword" label="Netconf Password" type="password" fullWidth value={this.state.NcPassword} onChange={(event) => { this.setState({ NcPassword: event.target.value }); }} /> + <TextField variant="standard" disabled={setting.readonly} spellCheck={false} autoFocus margin="dense" id="ncPort" label="Netconf Port" type="number" fullWidth value={this.state.NcPort || ""} onChange={(event) => { this.setState({ NcPort: +event.target.value }); }} /> + </TabContainer> : null} + {this.state.activeTab === 1 ? <TabContainer > + {this.state.ODLConfig && this.state.ODLConfig.length > 0 + ? this.state.ODLConfig.map((cfg, ind) => { + const panelId = `panel-${ind}`; + const deleteButton = (<IconButton + onClick={() => { + this.setState({ + ODLConfig: [ + ...this.state.ODLConfig.slice(0, ind), + ...this.state.ODLConfig.slice(ind + 1) + ] + }); + }} + size="large"><DeleteIcon /></IconButton>) + return ( + <Panel title={cfg.Server && `${cfg.User ? `${cfg.User}@` : ''}${cfg.Protocol}://${cfg.Server}:${cfg.Port}` || "new odl config"} key={panelId} panelId={panelId} activePanel={this.state.activeOdlConfig} customActionButtons={[deleteButton]} + onToggle={(id) => { this.setState({ activeOdlConfig: (this.state.activeOdlConfig === id) ? "" : (id || "") }); console.log("activeOdlConfig " + id); this.hideHostnameErrormessage(id) }} > + <div className={classes.alignInOneLine}> + <FormControl variant="standard" className={classes.left} margin={"dense"} > + <InputLabel htmlFor={`protocol-${ind}`}>Protocoll</InputLabel> + <Select variant="standard" value={cfg.Protocol} onChange={(e, v) => this.odlConfigValueChangeHandlerCreator(ind, "Protocol", e => v)} inputProps={{ name: `protocol-${ind}`, id: `protocol-${ind}` }} fullWidth > + <MenuItem value={"http"}>http</MenuItem> + <MenuItem value={"https"}>https</MenuItem> + </Select> + </FormControl> + <TextField variant="standard" className={classes.left} spellCheck={false} margin="dense" id="hostname" label="Hostname" type="text" value={cfg.Server} onChange={this.odlConfigValueChangeHandlerCreator(ind, "Server", e => e.target.value)} /> + <TextField variant="standard" className={classes.right} style={{ maxWidth: "65px" }} spellCheck={false} margin="dense" id="port" label="Port" type="number" value={cfg.Port || ""} onChange={this.odlConfigValueChangeHandlerCreator(ind, "Port", e => +e.target.value)} /> + </div> + { + this.state.isOdlConfigHostnameEmpty && + <Typography component={"div"} className={classes.left} color="error" gutterBottom>Please add a hostname.</Typography> + } + <div className={classes.alignInOneLine}> + <TextField variant="standard" className={classes.left} spellCheck={false} margin="dense" id="username" label="Username" type="text" value={cfg.User} onChange={this.odlConfigValueChangeHandlerCreator(ind, "User", e => e.target.value)} /> + <TextField variant="standard" className={classes.right} spellCheck={false} margin="dense" id="password" label="Password" type="password" value={cfg.Password} onChange={this.odlConfigValueChangeHandlerCreator(ind, "Password", e => e.target.value)} /> + </div> + <div className={classes.alignInOneLine}> + <FormControlLabel className={classes.right} control={<Checkbox checked={cfg.Trustall} onChange={this.odlConfigValueChangeHandlerCreator(ind, "Trustall", e => e.target.checked)} />} label="Trustall" /> + </div> + </Panel> + ); + }) + : (this.state.forceAddOdlConfig ? + <div className={classes.center} > + <Typography component={"div"} className={classes.title} color="error" gutterBottom>Please add at least one ODL auto connect configuration.</Typography> + </div> + : + <div className={classes.center} > + <Typography component={"div"} className={classes.title} color="textSecondary" gutterBottom>Please add an ODL auto connect configuration.</Typography> + </div> + ) + } + <Fab className={classes.fab} color="primary" aria-label="Add" onClick={() => this.setState({ + ODLConfig: [...this.state.ODLConfig, { Server: '', Port: 8181, Protocol: 'https', User: 'admin', Password: 'admin', Trustall: false }] + })} > <AddIcon /> </Fab> + + </TabContainer> : null} + + </DialogContent> + <DialogActions> + <Button color="inherit" onClick={(event) => { this.addConfig(event) }} > {setting.applyButtonText} </Button> + <Button onClick={(event) => { + this.onCancel(); + event.preventDefault(); + event.stopPropagation(); + this.resetPanel(); + }} color="secondary"> {setting.cancelButtonText} </Button> + </DialogActions> + </Dialog> + ); + } + + private addConfig = (event: any) => { + event.preventDefault(); + event.stopPropagation(); + + if (this.state.ODLConfig.length === 0) { + this.setState({ activeTab: 1, forceAddOdlConfig: true }); + } + else + if (this.state.ODLConfig.length > 0) { + for (let i = 0; i <= this.state.ODLConfig.length; i++) { + if (this.isHostnameEmpty(i)) { + this.setState({ activeOdlConfig: 'panel-' + i }) + this.setState({ isOdlConfigHostnameEmpty: true }) + return; + } + } + + this.onApply(Object.keys(this.state).reduce<MediatorConfig & { [kex: string]: any }>((acc, key) => { + // do not copy additional state properties + if (key !== "activeTab" && key !== "activeOdlConfig" && key !== "isOdlConfigHostnameEmpty" + && key !== "forceAddOdlConfig" && key !== "_initialMediatorConfig") acc[key] = (this.state as any)[key]; + return acc; + }, {} as MediatorConfig)); + this.resetPanel(); + } + } + + private resetPanel = () => { + this.setState({ forceAddOdlConfig: false, isOdlConfigHostnameEmpty: false, activeTab: 0 }); + } + + + private hideHostnameErrormessage = (panelId: string | null) => { + + if (panelId) { + let id = Number(panelId.split('-')[1]); + if (!this.isHostnameEmpty(id)) { + this.setState({ isOdlConfigHostnameEmpty: false }) + } + } + } + + private isHostnameEmpty = (id: number) => { + + const element = this.state.ODLConfig[id]; + if (element) { + if (!element.Server) { + return true; + } + else { + return false + } + + } else { + return null; + } + } + + private onApply = (config: MediatorConfig) => { + this.props.onClose && this.props.onClose(); + switch (this.props.mode) { + case EditMediatorConfigDialogMode.AddMediatorConfig: + config && this.props.addMediatorConfig(config); + break; + case EditMediatorConfigDialogMode.EditMediatorConfig: + config && this.props.updateMediatorConfig(config); + break; + case EditMediatorConfigDialogMode.RemoveMediatorConfig: + config && this.props.removeMediatorConfig(config); + break; + } + }; + + private onCancel = () => { + this.props.onClose && this.props.onClose(); + } + + static getDerivedStateFromProps(props: EditMediatorConfigDialogComponentProps, state: EditMediatorConfigDialogComponentState & { _initialMediatorConfig: MediatorConfig }): EditMediatorConfigDialogComponentState & { _initialMediatorConfig: MediatorConfig } { + if (props.mediatorConfig !== state._initialMediatorConfig) { + state = { + ...state, + ...props.mediatorConfig, + _initialMediatorConfig: props.mediatorConfig, + }; + } + return state; + } +} + +export const EditMediatorConfigDialog = withStyles(styles)(connect(mapProps, mapDispatch)(EditMediatorConfigDialogComponent)); +export default EditMediatorConfigDialog;
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/components/editMediatorServerDialog.tsx b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/components/editMediatorServerDialog.tsx new file mode 100644 index 000000000..c8b158749 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/components/editMediatorServerDialog.tsx @@ -0,0 +1,221 @@ +/** + * ============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 Button from '@mui/material/Button'; +import TextField from '@mui/material/TextField'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; + +import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; + +import { addAvaliableMediatorServerAsyncActionCreator, removeAvaliableMediatorServerAsyncActionCreator, updateAvaliableMediatorServerAsyncActionCreator } from '../actions/avaliableMediatorServersActions'; +import { MediatorServer } from '../models/mediatorServer'; +import { Typography } from '@mui/material'; + +export enum EditMediatorServerDialogMode { + None = "none", + AddMediatorServer = "addMediatorServer", + EditMediatorServer = "editMediatorServer", + RemoveMediatorServer = "removeMediatorServer", +} + +const mapDispatch = (dispatcher: IDispatcher) => ({ + addMediatorServer: (element: MediatorServer) => { + dispatcher.dispatch(addAvaliableMediatorServerAsyncActionCreator(element)); + }, + updateMediatorServer: (element: MediatorServer) => { + dispatcher.dispatch(updateAvaliableMediatorServerAsyncActionCreator(element)); + }, + removeMediatorServer: (element: MediatorServer) => { + dispatcher.dispatch(removeAvaliableMediatorServerAsyncActionCreator(element)); + }, +}); + +type DialogSettings = { + dialogTitle: string; + dialogDescription: string; + applyButtonText: string; + cancelButtonText: string; + readonly: boolean; +}; + +const settings: { [key: string]: DialogSettings } = { + [EditMediatorServerDialogMode.None]: { + dialogTitle: "", + dialogDescription: "", + applyButtonText: "", + cancelButtonText: "", + readonly: true, + }, + [EditMediatorServerDialogMode.AddMediatorServer]: { + dialogTitle: "Add Mediator Server", + dialogDescription: "", + applyButtonText: "Add", + cancelButtonText: "Cancel", + readonly: false, + }, + [EditMediatorServerDialogMode.EditMediatorServer]: { + dialogTitle: "Edit Mediator Server", + dialogDescription: "", + applyButtonText: "Update", + cancelButtonText: "Cancel", + readonly: false, + }, + [EditMediatorServerDialogMode.RemoveMediatorServer]: { + dialogTitle: "Remove Mediator Server", + dialogDescription: "", + applyButtonText: "Remove", + cancelButtonText: "Cancel", + readonly: true, + }, +}; + +type EditMediatorServerDialogComponentProps = Connect<undefined, typeof mapDispatch> & { + mode: EditMediatorServerDialogMode; + mediatorServer: MediatorServer; + onClose: () => void; +}; + +const urlRegex = RegExp("^https?://"); + +type EditMediatorServerDialogComponentState = MediatorServer & { errorMessage: string[] }; + +class EditMediatorServerDialogComponent extends React.Component<EditMediatorServerDialogComponentProps, EditMediatorServerDialogComponentState> { + constructor(props: EditMediatorServerDialogComponentProps) { + super(props); + + this.state = { + ...this.props.mediatorServer, + errorMessage: [] + }; + } + + areFieldsValid = () => { + return this.state.name.trim().length > 0 && this.state.url.trim().length > 0 && urlRegex.test(this.state.url); + } + + createErrorMessages = () => { + + let messages = []; + if (this.state.name.trim().length === 0 && this.state.url.trim().length === 0) { + messages.push("The server name and the url must not be empty.") + } + else + if (this.state.url.trim().length === 0) { + messages.push("The server url must not be empty.") + + } else if (this.state.name.trim().length === 0) { + messages.push("The server name must not be empty.") + } + + if (!urlRegex.test(this.state.url)) { + if (messages.length > 0) { + return messages.concat(["The server url must start with 'http(s)://'."]) + } else { + return ["The server url must start with 'http(s)://'."] + } + } + + return messages; + } + + + + render(): JSX.Element { + const setting = settings[this.props.mode]; + + return ( + <Dialog open={this.props.mode !== EditMediatorServerDialogMode.None}> + <DialogTitle id="form-dialog-title">{setting.dialogTitle}</DialogTitle> + <DialogContent> + <DialogContentText> + {setting.dialogDescription} + </DialogContentText> + {/* <TextField disabled spellCheck={false} autoFocus margin="dense" id="id" label="Id" type="text" fullWidth value={ this.state._id } onChange={(event)=>{ this.setState({_id: event.target.value}); } } /> */} + <TextField variant="standard" disabled={setting.readonly} spellCheck={false} margin="dense" id="name" label="Name" type="text" fullWidth value={this.state.name} onChange={(event) => { this.setState({ name: event.target.value }); }} /> + <TextField variant="standard" disabled={setting.readonly} spellCheck={false} margin="dense" id="url" label="Url" type="text" fullWidth value={this.state.url} onChange={(event) => { this.setState({ url: event.target.value }); }} /> + + <Typography id="errorMessage" component={"div"} color="error">{this.state.errorMessage.map((error, index) => <div key={index}>{error}</div>)}</Typography> + + </DialogContent> + <DialogActions> + <Button onClick={(event) => { + + if (this.areFieldsValid()) { + this.setState({ errorMessage: [] }); + this.onApply({ + id: this.state.id, + name: this.state.name, + url: this.state.url + }); + } else { + const errorMessage = this.createErrorMessages() + this.setState({ errorMessage: errorMessage }) + } + + event.preventDefault(); + event.stopPropagation(); + }} color="inherit" > {setting.applyButtonText} </Button> + <Button onClick={(event) => { + this.onCancel(); + this.setState({ errorMessage: [] }); + event.preventDefault(); + event.stopPropagation(); + }} color="secondary"> {setting.cancelButtonText} </Button> + </DialogActions> + </Dialog> + ) + } + + private onApply = (element: MediatorServer) => { + this.props.onClose && this.props.onClose(); + switch (this.props.mode) { + case EditMediatorServerDialogMode.AddMediatorServer: + element && this.props.addMediatorServer(element); + break; + case EditMediatorServerDialogMode.EditMediatorServer: + element && this.props.updateMediatorServer(element); + break; + case EditMediatorServerDialogMode.RemoveMediatorServer: + element && this.props.removeMediatorServer(element); + break; + } + }; + + private onCancel = () => { + this.props.onClose && this.props.onClose(); + } + + static getDerivedStateFromProps(props: EditMediatorServerDialogComponentProps, state: EditMediatorServerDialogComponentState & { _initialMediatorServer: MediatorServer }): EditMediatorServerDialogComponentState & { _initialMediatorServer: MediatorServer } { + if (props.mediatorServer !== state._initialMediatorServer) { + state = { + ...state, + ...props.mediatorServer, + _initialMediatorServer: props.mediatorServer, + }; + } + return state; + } +} + +export const EditMediatorServerDialog = connect(undefined, mapDispatch)(EditMediatorServerDialogComponent); +export default EditMediatorServerDialog;
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/components/refreshMediatorDialog.tsx b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/components/refreshMediatorDialog.tsx new file mode 100644 index 000000000..db1ef8771 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/components/refreshMediatorDialog.tsx @@ -0,0 +1,117 @@ +/** + * ============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 Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; + +import { avaliableMediatorServersReloadAction } from '../handlers/avaliableMediatorServersHandler'; +import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; + +import { MediatorServer } from '../models/mediatorServer'; + +export enum RefreshMediatorDialogMode { + None = "none", + RefreshMediatorTable = "RefreshMediatorTable", +} + +const mapDispatch = (dispatcher: IDispatcher) => ({ + refreshMediator: () => dispatcher.dispatch(avaliableMediatorServersReloadAction) +}); + +type DialogSettings = { + dialogTitle: string, + dialogDescription: string, + applyButtonText: string, + cancelButtonText: string, + enableMountIdEditor: boolean, + enableUsernameEditor: boolean, + enableExtendedEditor: boolean, +} + +const settings: { [key: string]: DialogSettings } = { + [RefreshMediatorDialogMode.None]: { + dialogTitle: "", + dialogDescription: "", + applyButtonText: "", + cancelButtonText: "", + enableMountIdEditor: false, + enableUsernameEditor: false, + enableExtendedEditor: false, + }, + [RefreshMediatorDialogMode.RefreshMediatorTable]: { + dialogTitle: "Do you want to refresh the Mediator table?", + dialogDescription: "", + applyButtonText: "Yes", + cancelButtonText: "Cancel", + enableMountIdEditor: true, + enableUsernameEditor: true, + enableExtendedEditor: true, + } +} + +type RefreshMediatorDialogComponentProps = Connect<undefined, typeof mapDispatch> & { + mode: RefreshMediatorDialogMode; + onClose: () => void; +}; + +type RefreshMediatorDialogComponentState = MediatorServer & { isNameValid: boolean, isHostSet: boolean }; + +class RefreshMediatorDialogComponent extends React.Component<RefreshMediatorDialogComponentProps, RefreshMediatorDialogComponentState> { + constructor(props: RefreshMediatorDialogComponentProps) { + super(props); + } + + render(): JSX.Element { + const setting = settings[this.props.mode]; + return ( + <Dialog open={this.props.mode !== RefreshMediatorDialogMode.None}> + <DialogTitle id="form-dialog-title" aria-label={`${setting.dialogTitle.replace(/ /g, "-").toLowerCase()}-dialog`}>{setting.dialogTitle}</DialogTitle> + <DialogContent> + <DialogContentText> + {setting.dialogDescription} + </DialogContentText> + </DialogContent> + <DialogActions> + <Button aria-label="dialog-confirm-button" onClick={(event) => { + this.onRefresh(); + }} color="inherit" > {setting.applyButtonText} </Button> + <Button aria-label="dialog-cancel-button" onClick={(event) => { + this.onCancel(); + }} color="secondary"> {setting.cancelButtonText} </Button> + </DialogActions> + </Dialog> + ); + } + + private onRefresh = () => { + this.props.refreshMediator(); + this.props.onClose(); + }; + + private onCancel = () => { + this.props.onClose(); + } +} + +export const RefreshMediatorDialog = connect(undefined, mapDispatch)(RefreshMediatorDialogComponent); +export default RefreshMediatorDialog;
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/components/showMeditaorInfoDialog.tsx b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/components/showMeditaorInfoDialog.tsx new file mode 100644 index 000000000..f8691ab2f --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/components/showMeditaorInfoDialog.tsx @@ -0,0 +1,107 @@ +/** + * ============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 React from 'react' +import { Dialog, DialogTitle, DialogContent, DialogActions, TextField, DialogContentText, Checkbox, Button, FormControlLabel, FormGroup } from '@mui/material'; +import { IApplicationState } from '../../../../framework/src/handlers/applicationStateHandler'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { connect, Connect } from '../../../../framework/src/flux/connect'; +import { MediatorConfigResponse } from '../models/mediatorServer'; +import { Panel } from '../../../../framework/src/components/material-ui/panel'; + +export enum MediatorInfoDialogMode { + None = "none", + ShowDetails = "showDetails" +} + +const mapProps = (state: IApplicationStoreState) => ({ supportedDevices: state.mediator.mediatorServerState.supportedDevices }) + +type ShowMediatorInfoDialogComponentProps = Connect<typeof mapProps, undefined> & +{ + config: MediatorConfigResponse, + mode: MediatorInfoDialogMode, + onClose: () => void +} + +type ShowMediatorInfoDialogComponentState = { + status: string, + devicetype: string, + activeOdlConfig: string +} + +/* +Displays all values of a mediator server +*/ +class ShowMediatorInfoDialogComponent extends React.Component<ShowMediatorInfoDialogComponentProps, ShowMediatorInfoDialogComponentState> { + + constructor(props: ShowMediatorInfoDialogComponentProps) { + super(props); + if (this.props.config) { + let deviceType = props.supportedDevices.find(element => element.id === this.props.config.DeviceType) + + this.state = { + status: props.config.pid > 0 ? "Running" : "Stopped", + devicetype: deviceType != undefined ? deviceType.device : 'none', + activeOdlConfig: '' + } + } + } + + onClose = (event: React.MouseEvent) => { + event.preventDefault(); + event.stopPropagation(); + this.props.onClose(); + } + + render() { + return ( + <Dialog open={this.props.mode !== MediatorInfoDialogMode.None} onBackdropClick={this.props.onClose} > + <DialogTitle>{this.props.config.Name}</DialogTitle> + <DialogContent> + <TextField variant="standard" disabled margin="dense" id="deviceIp" label="Device IP" fullWidth defaultValue={this.props.config.DeviceIp} /> + <TextField variant="standard" disabled margin="dense" id="deviceport" label="Device Port" fullWidth defaultValue={this.props.config.DevicePort} /> + <TextField variant="standard" disabled margin="dense" id="status" label="Status" fullWidth defaultValue={this.state.status} /> + <TextField variant="standard" disabled margin="dense" id="deviceType" label="Device Type" fullWidth defaultValue={this.state.devicetype} /> + <TextField variant="standard" disabled margin="dense" id="ncPort" label="Netconf Port" fullWidth defaultValue={this.props.config.NcPort} /> + <FormGroup> + <FormControlLabel control={<Checkbox disabled defaultChecked={this.props.config.IsNCConnected}></Checkbox>} label="Netconf Connection" /> + <FormControlLabel control={<Checkbox disabled defaultChecked={this.props.config.IsNeConnected}></Checkbox>} label="Network Element Connection" /> + <FormControlLabel control={<Checkbox disabled defaultChecked={this.props.config.fwactive}></Checkbox>} label="Firewall active" /> + </FormGroup> + { + this.props.config.ODLConfig.map((element, index) => + <Panel title={"ODL config " + (this.props.config.ODLConfig.length > 1 ? index + 1 : '')} key={index} panelId={'panel-' + index} activePanel={this.state.activeOdlConfig} onToggle={(id: string) => { this.setState({ activeOdlConfig: (this.state.activeOdlConfig === id) ? "" : (id || "") }); }}> + <TextField variant="standard" disabled margin="dense" defaultValue={element.Protocol + '://' + element.Server} label="Server" /> + <TextField variant="standard" disabled margin="dense" defaultValue={element.Port} label="Port" /> + <FormControlLabel control={<Checkbox disabled checked={element.Trustall} />} label="Trustall" /> + </Panel> + ) + } + + </DialogContent> + <DialogActions> + <Button onClick={this.onClose} color="inherit">Close</Button> + </DialogActions> + </Dialog> + ) + } + +} + +export const ShowMediatorInfoDialog = connect(mapProps)(ShowMediatorInfoDialogComponent) +export default ShowMediatorInfoDialog;
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/handlers/avaliableMediatorServersHandler.ts b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/handlers/avaliableMediatorServersHandler.ts new file mode 100644 index 000000000..c22252d20 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/handlers/avaliableMediatorServersHandler.ts @@ -0,0 +1,36 @@ +/** + * ============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 { createExternal,IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; +import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch'; + +import { MediatorServer } from '../models/mediatorServer'; +import { mediatorServerResourcePath } from '../services/mediatorService'; + +export interface IAvaliableMediatorServersState extends IExternalTableState<MediatorServer> { } + +// create eleactic search material data fetch handler +const avaliableMediatorServersSearchHandler = createSearchDataHandler<MediatorServer>(mediatorServerResourcePath); + +export const { + actionHandler: avaliableMediatorServersActionHandler, + createActions: createAvaliableMediatorServersActions, + createProperties: createAvaliableMediatorServersProperties, + reloadAction: avaliableMediatorServersReloadAction, + + // set value action, to change a value +} = createExternal<MediatorServer>(avaliableMediatorServersSearchHandler, appState => appState.mediator.avaliableMediatorServers);
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/handlers/mediatorAppRootHandler.ts b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/handlers/mediatorAppRootHandler.ts new file mode 100644 index 000000000..2642ec8cd --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/handlers/mediatorAppRootHandler.ts @@ -0,0 +1,43 @@ +/** + * ============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========================================================================== + */ +// main state handler + +import { combineActionHandler } from '../../../../framework/src/flux/middleware'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import { IAvaliableMediatorServersState, avaliableMediatorServersActionHandler } from './avaliableMediatorServersHandler' ; +import { MediatorServerState, mediatorServerHandler } from './mediatorServerHandler'; + +export interface IMediatorAppStoreState { + avaliableMediatorServers: IAvaliableMediatorServersState, + mediatorServerState: MediatorServerState, +} + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + mediator: IMediatorAppStoreState + } +} + +const actionHandlers = { + avaliableMediatorServers: avaliableMediatorServersActionHandler, + mediatorServerState: mediatorServerHandler, +}; + +export const mediatorAppRootHandler = combineActionHandler<IMediatorAppStoreState>(actionHandlers); +export default mediatorAppRootHandler; diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/handlers/mediatorServerHandler.ts b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/handlers/mediatorServerHandler.ts new file mode 100644 index 000000000..246634cbe --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/handlers/mediatorServerHandler.ts @@ -0,0 +1,120 @@ +/** + * ============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 { XmlFileInfo, MediatorConfig, BusySymbol, MediatorConfigResponse, MediatorServerDevice } from "../models/mediatorServer"; +import { IActionHandler } from "../../../../framework/src/flux/action"; +import { SetMediatorServerVersion, SetMediatorServerInfo, SetAllMediatorServerConfigurations, SetMediatorServerBusy, SetMediatorServerSupportedDevices, SetMediatorServerReachable } from "../actions/mediatorServerActions"; +import { SetMediatorBusyByName, UpdateMediatorConfig, AddMediatorConfig, RemoveMediatorConfig } from "../actions/mediatorConfigActions"; + +export type MediatorServerState = { + busy: boolean; + name: string | null; + url: string | null; + id: string | null; + serverVersion: string | null; + mediatorVersion: string | null; + nexmls: XmlFileInfo[]; + configurations: MediatorConfigResponse[]; + supportedDevices: MediatorServerDevice[]; + isReachable: boolean; +} + +const mediatorServerInit: MediatorServerState = { + busy: false, + name: null, + url: null, + id: null, + serverVersion: null, + mediatorVersion: null, + nexmls: [], + configurations: [], + supportedDevices: [], + isReachable: true +} + +export const mediatorServerHandler: IActionHandler<MediatorServerState> = (state = mediatorServerInit, action) => { + if (action instanceof SetMediatorServerBusy) { + state = { + ...state, + busy: action.isBusy + }; + } else if (action instanceof SetMediatorServerInfo) { + state = { + ...state, + name: action.name, + url: action.url, + id: action.id, + }; + } else if (action instanceof SetMediatorServerVersion) { + state = { + ...state, + serverVersion: action.versionInfo && action.versionInfo.server, + mediatorVersion: action.versionInfo && action.versionInfo.mediator, + nexmls: action.versionInfo && [...action.versionInfo.nexmls] || [], + }; + } else if (action instanceof SetAllMediatorServerConfigurations) { + state = { + ...state, + configurations: action.allConfigurations && action.allConfigurations.map(config => ({ ...config, busy: false })) || [], + }; + } else if (action instanceof SetMediatorServerSupportedDevices) { + state = { + ...state, + supportedDevices: action.devices || [], + }; + } else if (action instanceof SetMediatorBusyByName) { + const index = state.configurations.findIndex(config => config.Name === action.name); + if (index > -1) state = { + ...state, + configurations: [ + ...state.configurations.slice(0, index), + { ...state.configurations[index], [BusySymbol]: action.isBusy }, + ...state.configurations.slice(index + 1) + ] + }; + } else if (action instanceof AddMediatorConfig) { + state = { + ...state, + configurations: [ + ...state.configurations, + action.mediatorConfig + ] + }; + } else if (action instanceof UpdateMediatorConfig) { + const index = state.configurations.findIndex(config => config.Name === action.name); + if (index > -1) state = { + ...state, + configurations: [ + ...state.configurations.slice(0, index), + { ...action.mediatorConfig, [BusySymbol]: state.configurations[index][BusySymbol] }, + ...state.configurations.slice(index + 1) + ] + }; + } else if (action instanceof RemoveMediatorConfig) { + const index = state.configurations.findIndex(config => config.Name === action.name); + if (index > -1) state = { + ...state, + configurations: [ + ...state.configurations.slice(0, index), + ...state.configurations.slice(index + 1) + ] + }; + } else if( action instanceof SetMediatorServerReachable){ + state = {...state, isReachable: action.isReachable} + } + return state; +}
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/index.html b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/index.html new file mode 100644 index 000000000..95bf9ec6b --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/index.html @@ -0,0 +1,29 @@ +<!DOCTYPE html> +<html lang="en"> + +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <meta http-equiv="X-UA-Compatible" content="ie=edge"> + <!-- <link rel="stylesheet" href="./vendor.css"> --> + <title>Mediator App</title> +</head> + +<body> + <div id="app"></div> + <script type="text/javascript" src="./require.js"></script> + <script type="text/javascript" src="./config.js"></script> + <script> + // run the application + require(["app", "mediatorApp" ,"connectApp","inventoryApp","faultApp","helpApp"], function (app, mediatorApp, connectApp,inventoryApp,faultApp,helpApp) { + mediatorApp.register(); + connectApp.register(); + inventoryApp.register(); + faultApp.register(); + helpApp.register(); + app("./app.tsx").runApplication(); + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/models/mediatorServer.ts b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/models/mediatorServer.ts new file mode 100644 index 000000000..6ab6db8b3 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/models/mediatorServer.ts @@ -0,0 +1,77 @@ +/** + * ============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========================================================================== + */ +export type MediatorServer = { + id: string; + name: string; + url: string; +} + +export type XmlFileInfo = { + filename: string; + version: string; +} + +export type MediatorServerVersionInfo = { + mediator: string; + server: string; + nexmls: XmlFileInfo[]; +} + +export type ODLConfig = { + User: string; + Password: string; + Port: number; + Protocol: "http" | "https"; + Server: string; + Trustall: boolean; +}; + +export const BusySymbol = Symbol("Busy"); + +export type MediatorConfig = { + Name: string; + DeviceIp: string; + DevicePort: number; + DeviceType: number; + TrapPort: number; + NcUsername: string; + NcPassword: string; + NcPort: number; + NeXMLFile: string; + ODLConfig: ODLConfig[]; +} + +export type MediatorConfigResponse = MediatorConfig & { + IsNCConnected: boolean; + IsNeConnected: boolean; + autorun: boolean; + fwactive: boolean; + islocked: boolean; + ncconnections:{}[]; + pid: number; + // extended properties + [BusySymbol]: boolean; +} + +export type MediatorServerDevice = { + id: number; // DeviceType + device: string; + vendor: string; + version: string; + xml: string; // NeXMLFile +}
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/plugin.tsx b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/plugin.tsx new file mode 100644 index 000000000..1c30cfc54 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/plugin.tsx @@ -0,0 +1,83 @@ +/** + * ============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========================================================================== + */ +// app configuration and main entry point for the app + +import React from "react"; +import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; + +import applicationManager from '../../../framework/src/services/applicationManager'; + +import { connect, Connect, IDispatcher } from '../../../framework/src/flux/connect'; + +import { mediatorAppRootHandler } from './handlers/mediatorAppRootHandler'; +import { avaliableMediatorServersReloadAction } from "./handlers/avaliableMediatorServersHandler"; + +import { MediatorApplication } from "./views/mediatorApplication"; +import { MediatorServerSelection } from "./views/mediatorServerSelection"; +import { initializeMediatorServerAsyncActionCreator } from "./actions/mediatorServerActions"; + +const appIcon = require('./assets/icons/mediatorAppIcon.svg'); // select app icon + +let currentMediatorServerId: string | undefined = undefined; + +const mapDisp = (dispatcher: IDispatcher) => ({ + loadMediatorServer : (mediatorServerId: string) => dispatcher.dispatch(initializeMediatorServerAsyncActionCreator(mediatorServerId)), +}); + +const MediatorServerRouteAdapter = connect(undefined, mapDisp)((props: RouteComponentProps<{ mediatorServerId: string }> & Connect<undefined, typeof mapDisp>) => { + if (currentMediatorServerId !== props.match.params.mediatorServerId) { + // route parameter has changed + currentMediatorServerId = props.match.params.mediatorServerId || undefined; + // Hint: This timeout is need, since it is not recommended to change the state while rendering is in progress ! + window.setTimeout(() => { + if (currentMediatorServerId) { + props.loadMediatorServer(currentMediatorServerId); + } + }); + } + return ( + <MediatorApplication /> + ) +}); + +type AppProps = RouteComponentProps & Connect; + +const App = (props: AppProps) => ( + <Switch> + <Route exact path={ `${ props.match.path }` } component={ MediatorServerSelection } /> + <Route path={ `${ props.match.path }/:mediatorServerId` } component={ MediatorServerRouteAdapter } /> + <Redirect to={ `${ props.match.path }` } /> + </Switch> +); + +const FinalApp = withRouter(connect()(App)); + +export function register() { + const applicationApi = applicationManager.registerApplication({ + name: "mediator", + icon: appIcon, + rootComponent: FinalApp, + rootActionHandler: mediatorAppRootHandler, + menuEntry: "Mediator" + }); + + // prefetch all available mediator servers + applicationApi.applicationStoreInitialized.then(applicationStore => { + applicationStore.dispatch(avaliableMediatorServersReloadAction) + }); +}; diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/services/mediatorService.ts b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/services/mediatorService.ts new file mode 100644 index 000000000..ede681776 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/services/mediatorService.ts @@ -0,0 +1,204 @@ +/** + * ============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 $ from 'jquery'; + +import { requestRest, formEncode } from '../../../../framework/src/services/restService'; +import { MediatorServer, MediatorServerVersionInfo, MediatorConfig, MediatorServerDevice, MediatorConfigResponse } from '../models/mediatorServer'; +import { PostResponse, DeleteResponse, Result } from '../../../../framework/src/models'; + +export const mediatorServerResourcePath = "mediator-server"; + +type MediatorServerResponse<TData> = { code: number, data: TData }; +type IndexableMediatorServer = MediatorServer & { [key: string]: any; }; + +/** + * Represents a web api accessor service for all mediator server actions. + */ +class MediatorService { + /** + * Inserts data into the mediator servers table. + */ + public async insertMediatorServer(server: IndexableMediatorServer): Promise<PostResponse | null> { + const path = `/restconf/operations/data-provider:create-mediator-server`; + + const data = { + "url": server.url, + "name": server.name + } + + const result = await requestRest<PostResponse>(path, { method: "POST", body: JSON.stringify({ input: data }) }); + return result || null; + } + + /** + * Updates data into the mediator servers table. + */ + public async updateMediatorServer(server: IndexableMediatorServer): Promise<PostResponse | null> { + const path = `/restconf/operations/data-provider:update-mediator-server`; + + const data = { + "id": server.id, + "url": server.url, + "name": server.name + } + + const result = await requestRest<PostResponse>(path, { method: "POST", body: JSON.stringify({ input: data }) }); + return result || null; + } + + /** + * Deletes data from the mediator servers table. + */ + public async deleteMediatorServer(server: MediatorServer): Promise<DeleteResponse | null> { + const path = `/restconf/operations/data-provider:delete-mediator-server`; + + const data = { + "id": server.id, + } + + const result = await requestRest<DeleteResponse>(path, { method: "POST", body: JSON.stringify({ input: data }) }); + return result || null; + } + + public async getMediatorServerById(serverId: string): Promise<MediatorServer | null> { + const path = `/restconf/operations/data-provider:read-mediator-server-list`; + + const data = { "filter": [{ "property": "id", "filtervalue": serverId }] } + + + const result = await requestRest<Result<MediatorServer>>(path, { method: "POST", body: JSON.stringify({ input: data }) }); + + if (result && result["data-provider:output"].data[0]) { + const firstResult = result["data-provider:output"].data[0]; + + return { + id: firstResult.id, + name: firstResult.name, + url: firstResult.url + } + } + else { + return null; + } + } + + // https://cloud-highstreet-technologies.com/wiki/doku.php?id=att:ms:api + + private async accassMediatorServer<TData = {}>(mediatorServerId: string, task: string, data?: {}): Promise<MediatorServerResponse<TData> | null> { + const path = `ms/${mediatorServerId}/api/'?task=${task}`; + const result = (await requestRest<string>(path, { + method: data ? "POST" : "GET", + headers: { + 'Content-Type': 'application/x-www-form-urlencoded' + }, + body: data ? formEncode({ + ...data, + ...{ task: task } + }) : null + }, true)) || null; + + return result ? JSON.parse(result) as { code: number, data: TData } : null; + } + /* + private accassMediatorServer<TData = {}>(mediatorServerId: string, task: string, data?: {}): Promise<MediatorServerResponse<TData> | null> { + const path = `ms/${mediatorServerId}/api/?task=${task}`; + return new Promise<{ code: number, data: TData }>((resolve, reject) => { + $.ajax({ + method: data ? 'POST' : 'GET', + url: path, + data: { ...{ task: task }, ...data }, + //contentType: 'application/json' + }).then((result: any) => { + if (typeof result === "string") { + resolve(JSON.parse(result)); + } else { + resolve(result); + }; + }); + }); + }*/ + + public async getMediatorServerVersion(mediatorServerId: string): Promise<MediatorServerVersionInfo | null> { + const result = await this.accassMediatorServer<MediatorServerVersionInfo>(mediatorServerId, 'version'); + if (result && result.code === 1) return result.data; + return null; + } + + public async getMediatorServerAllConfigs(mediatorServerId: string): Promise<MediatorConfigResponse[] | null> { + const result = await this.accassMediatorServer<MediatorConfigResponse[]>(mediatorServerId, 'getconfig'); + if (result && result.code === 1) return result.data; + return null; + } + + public async getMediatorServerConfigByName(mediatorServerId: string, name: string): Promise<MediatorConfigResponse | null> { + const result = await this.accassMediatorServer<MediatorConfigResponse[]>(mediatorServerId, `getconfig&name=${name}`); + if (result && result.code === 1 && result.data && result.data.length === 1) return result.data[0]; + return null; + } + + public async getMediatorServerSupportedDevices(mediatorServerId: string): Promise<MediatorServerDevice[] | null> { + const result = await this.accassMediatorServer<MediatorServerDevice[]>(mediatorServerId, 'getdevices'); + if (result && result.code === 1) return result.data; + return null; + } + + public async startMediatorByName(mediatorServerId: string, name: string): Promise<string | null> { + const result = await this.accassMediatorServer<string>(mediatorServerId, `start&name=${name}`); + if (result && result.code === 1) return result.data; + return null; + } + + public async stopMediatorByName(mediatorServerId: string, name: string): Promise<string | null> { + const result = await this.accassMediatorServer<string>(mediatorServerId, `stop&name=${name}`); + if (result && result.code === 1) return result.data; + return null; + } + + public async createMediatorConfig(mediatorServerId: string, config: MediatorConfig): Promise<string | null> { + const result = await this.accassMediatorServer<string>(mediatorServerId, 'create', { config: JSON.stringify(config) }); + if (result && result.code === 1) return result.data; + return null; + } + + public async updateMediatorConfigByName(mediatorServerId: string, config: MediatorConfig): Promise<string | null> { + const result = await this.accassMediatorServer<string>(mediatorServerId, 'update', { config: JSON.stringify(config) }); + if (result && result.code === 1) return result.data; + return null; + } + + public async deleteMediatorConfigByName(mediatorServerId: string, name: string): Promise<string | null> { + const result = await this.accassMediatorServer<string>(mediatorServerId, `delete&name=${name}`); + if (result && result.code === 1) return result.data; + return null; + } + + public async getMediatorServerFreeNcPorts(mediatorServerId: string, limit?: number): Promise<number[] | null> { + const result = await this.accassMediatorServer<number[]>(mediatorServerId, 'getncports', { limit }); + if (result && result.code === 1) return result.data; + return null; + } + + public async getMediatorServerFreeSnmpPorts(mediatorServerId: string, limit?: number): Promise<number[] | null> { + const result = await this.accassMediatorServer<number[]>(mediatorServerId, 'getsnmpports', { limit }); + if (result && result.code === 1) return result.data; + return null; + } +} + +export const mediatorService = new MediatorService; +export default mediatorService;
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx new file mode 100644 index 000000000..03ce4532e --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx @@ -0,0 +1,291 @@ +/** + * ============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 React from 'react'; +import { Theme, Tooltip } from '@mui/material'; + +import { WithStyles } from '@mui/styles'; +import createStyles from '@mui/styles/createStyles'; +import withStyles from '@mui/styles/withStyles'; + +import AddIcon from '@mui/icons-material/Add'; +import IconButton from '@mui/material/IconButton'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import InfoIcon from '@mui/icons-material/Info'; +import StartIcon from '@mui/icons-material/PlayArrow'; +import StopIcon from '@mui/icons-material/Stop'; + +import CircularProgress from '@mui/material/CircularProgress' + +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import MaterialTable, { MaterialTableCtorType, ColumnType } from '../../../../framework/src/components/material-table'; + +import { MediatorConfig, BusySymbol, MediatorConfigResponse } from '../models/mediatorServer'; +import EditMediatorConfigDialog, { EditMediatorConfigDialogMode } from '../components/editMediatorConfigDialog'; +import { startMediatorByNameAsyncActionCreator, stopMediatorByNameAsyncActionCreator } from '../actions/mediatorConfigActions'; +import mediatorService from '../services/mediatorService'; +import { ShowMediatorInfoDialog, MediatorInfoDialogMode } from '../components/showMeditaorInfoDialog' + +const styles = (theme: Theme) => createStyles({ + root: { + display: 'flex', + flexDirection: 'column', + flex: '1', + }, + formControl: { + margin: theme.spacing(1), + minWidth: 300, + }, + button: { + margin: 0, + padding: "6px 6px", + minWidth: 'unset' + }, + spacer: { + marginLeft: theme.spacing(1), + marginRight: theme.spacing(1), + display: "inline" + }, + progress: { + flex: '1 1 100%', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + pointerEvents: 'none' + } +}); + +const mapProps = (state: IApplicationStoreState) => ({ + serverName: state.mediator.mediatorServerState.name, + serverUrl: state.mediator.mediatorServerState.url, + serverId: state.mediator.mediatorServerState.id, + serverVersion: state.mediator.mediatorServerState.serverVersion, + mediatorVersion: state.mediator.mediatorServerState.mediatorVersion, + configurations: state.mediator.mediatorServerState.configurations, + supportedDevices: state.mediator.mediatorServerState.supportedDevices, + busy: state.mediator.mediatorServerState.busy, + isReachable: state.mediator.mediatorServerState.isReachable +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + startMediator: (name: string) => dispatcher.dispatch(startMediatorByNameAsyncActionCreator(name)), + stopMediator: (name: string) => dispatcher.dispatch(stopMediatorByNameAsyncActionCreator(name)), +}); + +const emptyMediatorConfig: MediatorConfig = { + Name: "", + DeviceIp: "127.0.0.1", + DevicePort: 161, + NcUsername: "admin", + NcPassword: "admin", + DeviceType: -1, + NcPort: 4020, + TrapPort: 10020, + NeXMLFile: "", + ODLConfig: [] +}; + +const MediatorServerConfigurationsTable = MaterialTable as MaterialTableCtorType<MediatorConfigResponse>; +const MediatorServerUnreachableTable = MaterialTable as MaterialTableCtorType<{ Name: string, status: string, ipAdress: string, device: string, actions: string }> + +type MediatorApplicationComponentProps = Connect<typeof mapProps, typeof mapDispatch> & WithStyles<typeof styles>; + +type MediatorServerSelectionComponentState = { + busy: boolean, + mediatorConfigToEdit: MediatorConfig, + mediatorConfigEditorMode: EditMediatorConfigDialogMode, + mediatorShowInfoMode: MediatorInfoDialogMode, + mediatorConfigToDisplay: MediatorConfigResponse | null +} + +class MediatorApplicationComponent extends React.Component<MediatorApplicationComponentProps, MediatorServerSelectionComponentState> { + + constructor(props: MediatorApplicationComponentProps) { + super(props); + + this.state = { + busy: false, + mediatorConfigToEdit: emptyMediatorConfig, + mediatorConfigEditorMode: EditMediatorConfigDialogMode.None, + mediatorShowInfoMode: MediatorInfoDialogMode.None, + mediatorConfigToDisplay: null + } + } + + render() { + const { classes } = this.props; + + const renderActions = (rowData: MediatorConfigResponse) => ( + <> + <div className={classes.spacer}> + <Tooltip disableInteractive title={"Start"} > + <IconButton disabled={rowData[BusySymbol]} className={classes.button} size="large"> + <StartIcon onClick={(event) => { event.preventDefault(); event.stopPropagation(); this.props.startMediator(rowData.Name); }} /> + </IconButton> + </Tooltip> + <Tooltip disableInteractive title={"Stop"} > + <IconButton disabled={rowData[BusySymbol]} className={classes.button} size="large"> + <StopIcon onClick={(event) => { event.preventDefault(); event.stopPropagation(); this.props.stopMediator(rowData.Name); }} /> + </IconButton> + </Tooltip> + </div> + <div className={classes.spacer}> + <Tooltip disableInteractive title={"Info"} ><IconButton + className={classes.button} + onClick={(event) => { this.onOpenInfoDialog(event, rowData) }} + size="large"><InfoIcon /></IconButton></Tooltip> + </div> + <div className={classes.spacer}> + {process.env.NODE_ENV === "development" ? <Tooltip disableInteractive title={"Edit"} ><IconButton + disabled={rowData[BusySymbol]} + className={classes.button} + onClick={event => this.onOpenEditConfigurationDialog(event, rowData)} + size="large"><EditIcon /></IconButton></Tooltip> : null} + <Tooltip disableInteractive title={"Remove"} ><IconButton + disabled={rowData[BusySymbol]} + className={classes.button} + onClick={event => this.onOpenRemoveConfigutationDialog(event, rowData)} + size="large"><DeleteIcon /></IconButton></Tooltip> + </div> + </> + ); + + const addMediatorConfigAction = { icon: AddIcon, tooltip: 'Add', ariaLabel: 'add-element', onClick: this.onOpenAddConfigurationDialog }; + + return ( + <div className={classes.root}> + {this.props.busy || this.state.busy + ? <div className={classes.progress}> <CircularProgress color={"secondary"} size={48} /> </div> + : + + this.props.isReachable ? + + <MediatorServerConfigurationsTable defaultSortColumn={"Name"} tableId={null} defaultSortOrder="asc" stickyHeader title={this.props.serverName || ''} customActionButtons={[addMediatorConfigAction]} idProperty={"Name"} rows={this.props.configurations} asynchronus columns={[ + { property: "Name", title: "Mediator", type: ColumnType.text }, + { property: "Status", title: "Status", type: ColumnType.custom, customControl: ({ rowData }) => rowData.pid ? (<span>Running</span>) : (<span>Stopped</span>) }, + { property: "DeviceIp", title: "IP Adress", type: ColumnType.text }, + { + property: "Device", title: "Device", type: ColumnType.custom, customControl: ({ rowData }) => { + const dev = this.props.supportedDevices && this.props.supportedDevices.find(dev => dev.id === rowData.DeviceType); + return ( + <span> {dev && `${dev.vendor} - ${dev.device} (${dev.version || '0.0.0'})`} </span> + ) + } + }, + { property: "actions", title: "Actions", type: ColumnType.custom, customControl: ({ rowData }) => renderActions(rowData) }, + ]} /> + : + <MediatorServerUnreachableTable title={this.props.serverName || ''} tableId={null} idProperty={"Name"} disableFilter={true} disableSorting={true} enableSelection={false} rows={[{ Name: '', status: "Mediator server not found.", ipAdress: '', device: '', actions: '' }]} columns={[ + { property: "Name", title: "Mediator", type: ColumnType.text }, + { property: "status", title: "Status", type: ColumnType.text }, + { property: "ipAdress", title: "IP Adress", type: ColumnType.text }, + { property: "device", title: "Device", type: ColumnType.text }, + { property: "actions", title: "Actions", type: ColumnType.text }, + + ]} /> + } + + <EditMediatorConfigDialog + mediatorConfig={this.state.mediatorConfigToEdit} + mode={this.state.mediatorConfigEditorMode} + onClose={this.onCloseEditMediatorConfigDialog} /> + + { + + this.state.mediatorShowInfoMode != MediatorInfoDialogMode.None && + <ShowMediatorInfoDialog + config={this.state.mediatorConfigToDisplay as MediatorConfigResponse} + mode={this.state.mediatorShowInfoMode} + onClose={this.onCloseInfoDialog} /> + } + </div> + ); + } + + private onOpenInfoDialog = (event: React.MouseEvent<HTMLElement>, configEntry: MediatorConfigResponse) => { + event.stopPropagation(); + event.preventDefault(); + this.setState({ mediatorShowInfoMode: MediatorInfoDialogMode.ShowDetails, mediatorConfigToDisplay: configEntry }) + } + + private onCloseInfoDialog = () => { + this.setState({ mediatorShowInfoMode: MediatorInfoDialogMode.None, mediatorConfigToDisplay: null }) + } + + private onOpenAddConfigurationDialog = () => { + // Tries to determine a free port for netconf listener and snpm listener + // it it could not determine free ports the dialog will open any way + // those ports should not be configured from the fontend, furthermore + // the backend should auto configure them and tell the user the result + // after the creation process. + this.setState({ + busy: true, + }); + this.props.serverId && Promise.all([ + mediatorService.getMediatorServerFreeNcPorts(this.props.serverId, 1), + mediatorService.getMediatorServerFreeSnmpPorts(this.props.serverId, 1), + ]).then(([freeNcPorts, freeSnmpPorts]) => { + if (freeNcPorts && freeSnmpPorts && freeNcPorts.length > 0 && freeSnmpPorts.length > 0) { + this.setState({ + busy: false, + mediatorConfigEditorMode: EditMediatorConfigDialogMode.AddMediatorConfig, + mediatorConfigToEdit: { + ...emptyMediatorConfig, + NcPort: freeNcPorts[0], + TrapPort: freeSnmpPorts[0], + }, + }); + } else { + this.setState({ + busy: false, + mediatorConfigEditorMode: EditMediatorConfigDialogMode.AddMediatorConfig, + mediatorConfigToEdit: { ...emptyMediatorConfig }, + }); + } + }) + + } + + private onOpenEditConfigurationDialog = (event: React.MouseEvent<HTMLElement>, configEntry: MediatorConfig) => { + event.preventDefault(); + event.stopPropagation(); + this.setState({ + mediatorConfigEditorMode: EditMediatorConfigDialogMode.EditMediatorConfig, + mediatorConfigToEdit: configEntry, + }); + } + + private onOpenRemoveConfigutationDialog = (event: React.MouseEvent<HTMLElement>, configEntry: MediatorConfig) => { + event.preventDefault(); + event.stopPropagation(); + this.setState({ + mediatorConfigEditorMode: EditMediatorConfigDialogMode.RemoveMediatorConfig, + mediatorConfigToEdit: configEntry, + }); + } + + private onCloseEditMediatorConfigDialog = () => { + this.setState({ + mediatorConfigEditorMode: EditMediatorConfigDialogMode.None, + mediatorConfigToEdit: emptyMediatorConfig, + }); + } +} + +export const MediatorApplication = withStyles(styles)(connect(mapProps, mapDispatch)(MediatorApplicationComponent)); diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx new file mode 100644 index 000000000..fb12f2608 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx @@ -0,0 +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 React from 'react'; +import { Theme, Tooltip } from '@mui/material'; + +import { WithStyles } from '@mui/styles'; +import withStyles from '@mui/styles/withStyles'; +import createStyles from '@mui/styles/createStyles'; + +import AddIcon from '@mui/icons-material/Add'; +import IconButton from '@mui/material/IconButton'; +import EditIcon from '@mui/icons-material/Edit'; +import DeleteIcon from '@mui/icons-material/Delete'; +import Refresh from '@mui/icons-material/Refresh'; + +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { connect, IDispatcher, Connect } from '../../../../framework/src/flux/connect'; +import MaterialTable, { MaterialTableCtorType, ColumnType } from '../../../../framework/src/components/material-table'; + +import { createAvaliableMediatorServersProperties, createAvaliableMediatorServersActions } from '../handlers/avaliableMediatorServersHandler'; + +import { MediatorServer } from '../models/mediatorServer'; +import EditMediatorServerDialog, { EditMediatorServerDialogMode } from '../components/editMediatorServerDialog'; +import RefreshMediatorDialog, { RefreshMediatorDialogMode } from '../components/refreshMediatorDialog'; +import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions'; + +const MediatorServersTable = MaterialTable as MaterialTableCtorType<MediatorServer>; + +const styles = (theme: Theme) => createStyles({ + button: { + margin: 0, + padding: "6px 6px", + minWidth: 'unset', + }, + spacer: { + marginLeft: theme.spacing(1), + marginRight: theme.spacing(1), + display: "inline", + }, +}); + +const mapProps = (state: IApplicationStoreState) => ({ + mediatorServersProperties: createAvaliableMediatorServersProperties(state), +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + mediatorServersActions: createAvaliableMediatorServersActions(dispatcher.dispatch), + selectMediatorServer: (mediatorServerId: string) => mediatorServerId && dispatcher.dispatch(new NavigateToApplication("mediator", mediatorServerId)), +}); + +const emptyMediatorServer: MediatorServer = { + id: "", + name: "", + url: "" +}; + +type MediatorServerSelectionComponentProps = Connect<typeof mapProps, typeof mapDispatch> & WithStyles<typeof styles>; + +type MediatorServerSelectionComponentState = { + mediatorServerToEdit: MediatorServer, + mediatorServerEditorMode: EditMediatorServerDialogMode, + refreshMediatorEditorMode: RefreshMediatorDialogMode +} + +let initialSorted = false; + +class MediatorServerSelectionComponent extends React.Component<MediatorServerSelectionComponentProps, MediatorServerSelectionComponentState> { + + constructor(props: MediatorServerSelectionComponentProps) { + super(props); + + this.state = { + mediatorServerEditorMode: EditMediatorServerDialogMode.None, + mediatorServerToEdit: emptyMediatorServer, + refreshMediatorEditorMode: RefreshMediatorDialogMode.None + } + } + + render() { + const { classes } = this.props; + const refreshMediatorAction = { + icon: Refresh, tooltip: 'Refresh Mediator Server Table', ariaLabel:'refresh', onClick: () => { + this.setState({ + refreshMediatorEditorMode: RefreshMediatorDialogMode.RefreshMediatorTable + }); + } + }; + + const addMediatorServerActionButton = { + icon: AddIcon, tooltip: 'Add', ariaLabel:'add-element', onClick: () => { + this.setState({ + mediatorServerEditorMode: EditMediatorServerDialogMode.AddMediatorServer, + mediatorServerToEdit: emptyMediatorServer, + }); + } + }; + return <> + <MediatorServersTable stickyHeader title={"Mediator"} tableId={null} customActionButtons={[refreshMediatorAction, addMediatorServerActionButton]} idProperty={"id"} + {...this.props.mediatorServersActions} {...this.props.mediatorServersProperties} columns={[ + { property: "name", title: "Name", type: ColumnType.text }, + { property: "url", title: "Url", type: ColumnType.text }, + { + property: "actions", title: "Actions", type: ColumnType.custom, customControl: ({ rowData }) => ( + <div className={classes.spacer}> + <Tooltip disableInteractive title={"Edit"} ><IconButton + className={classes.button} + onClick={event => { this.onEditMediatorServer(event, rowData); }} + size="large"><EditIcon /></IconButton></Tooltip> + <Tooltip disableInteractive title={"Remove"} ><IconButton + className={classes.button} + onClick={event => { this.onRemoveMediatorServer(event, rowData); }} + size="large"><DeleteIcon /></IconButton></Tooltip> + </div> + ) + } + ]} onHandleClick={this.onSelectMediatorServer} /> + <EditMediatorServerDialog + mediatorServer={this.state.mediatorServerToEdit} + mode={this.state.mediatorServerEditorMode} + onClose={this.onCloseEditMediatorServerDialog} /> + <RefreshMediatorDialog + mode={this.state.refreshMediatorEditorMode} + onClose={this.onCloseRefreshMediatorDialog} + /> + </>; + } + + public componentDidMount() { + + if (!initialSorted) { + initialSorted = true; + this.props.mediatorServersActions.onHandleRequestSort("name"); + } else { + this.props.mediatorServersActions.onRefresh(); + } + } + + private onSelectMediatorServer = (event: React.MouseEvent<HTMLElement>, server: MediatorServer) => { + event.preventDefault(); + event.stopPropagation(); + this.props.selectMediatorServer(server && server.id); + + } + + private onEditMediatorServer = (event: React.MouseEvent<HTMLElement>, server: MediatorServer) => { + event.preventDefault(); + event.stopPropagation(); + this.setState({ + mediatorServerEditorMode: EditMediatorServerDialogMode.EditMediatorServer, + mediatorServerToEdit: server, + }); + } + + private onRemoveMediatorServer = (event: React.MouseEvent<HTMLElement>, server: MediatorServer) => { + event.preventDefault(); + event.stopPropagation(); + this.setState({ + mediatorServerEditorMode: EditMediatorServerDialogMode.RemoveMediatorServer, + mediatorServerToEdit: server, + }); + } + + private onCloseEditMediatorServerDialog = () => { + this.setState({ + mediatorServerEditorMode: EditMediatorServerDialogMode.None, + mediatorServerToEdit: emptyMediatorServer, + }); + } + private onCloseRefreshMediatorDialog = () => { + this.setState({ + refreshMediatorEditorMode: RefreshMediatorDialogMode.None + }); + } +} + + +export const MediatorServerSelection = withStyles(styles)(connect(mapProps, mapDispatch)(MediatorServerSelectionComponent)); +export default MediatorServerSelection;
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/tsconfig.json b/sdnr/wt-odlux/odlux/apps/mediatorApp/tsconfig.json new file mode 100644 index 000000000..c95005660 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/tsconfig.json @@ -0,0 +1,38 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": true, + "allowUnreachableCode": false, + "allowUnusedLabels": false, + "noFallthroughCasesInSwitch": true, + "noImplicitAny": true, + "noImplicitReturns": true, + "noImplicitThis": true, + "strictNullChecks": true, + "pretty": true, + "newLine": "LF", + "module": "es2015", + "target": "es2016", + "moduleResolution": "node", + "experimentalDecorators": true, + "jsx": "preserve", + "lib": [ + "dom", + "es2015", + "es2016" + ], + "types": [ + "node", + "prop-types", + "react", + "react-dom" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/tslint.json b/sdnr/wt-odlux/odlux/apps/mediatorApp/tslint.json new file mode 100644 index 000000000..b5143e92c --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/tslint.json @@ -0,0 +1,4 @@ +{ + "rules":{ + } +}
\ No newline at end of file diff --git a/sdnr/wt-odlux/odlux/apps/mediatorApp/webpack.config.js b/sdnr/wt-odlux/odlux/apps/mediatorApp/webpack.config.js new file mode 100644 index 000000000..813c28f37 --- /dev/null +++ b/sdnr/wt-odlux/odlux/apps/mediatorApp/webpack.config.js @@ -0,0 +1,158 @@ +/** + * Webpack 4 configuration file + * see https://webpack.js.org/configuration/ + * see https://webpack.js.org/configuration/dev-server/ + */ + +"use strict"; + +const path = require("path"); +const webpack = require("webpack"); +const CopyWebpackPlugin = require("copy-webpack-plugin"); +const TerserPlugin = require('terser-webpack-plugin'); + +// const __dirname = (path => path.replace(/^([a-z]\:)/, c => c.toUpperCase()))(process.__dirname()); + +module.exports = (env) => { + const distPath = path.resolve(__dirname, env === "release" ? "." : "../..", "dist"); + const frameworkPath = path.resolve(__dirname, env === "release" ? "../../framework" : "../..", "dist"); + return [{ + name: "App", + + mode: "none", //disable default behavior + + target: "web", + + context: path.resolve(__dirname, "src"), + + entry: { + mediatorApp: ["./plugin.tsx"] + }, + + devtool: env === "release" ? false : "source-map", + + resolve: { + extensions: [".ts", ".tsx", ".js", ".jsx"] + }, + + output: { + path: distPath, + filename: "[name].js", + library: "[name]", + libraryTarget: "umd2", + chunkFilename: "[name].js" + }, + module: { + rules: [{ + test: /\.tsx?$/, + exclude: /node_modules/, + use: [{ + loader: "babel-loader" + }, { + loader: "ts-loader" + }] + }, { + test: /\.jsx?$/, + exclude: /node_modules/, + use: [{ + loader: "babel-loader" + }] + },{ + //don't minify images + test: /\.(png|gif|jpg|svg)$/, + use: [{ + loader: 'url-loader', + options: { + limit: 10, + name: './images/[name].[ext]' + } + }] + }] + }, + + optimization: { + noEmitOnErrors: true, + namedModules: env !== "release", + minimize: env === "release", + minimizer: env !== "release" ? [] : [new TerserPlugin({ + terserOptions: { + warnings: false, // false, true, "verbose" + compress: { + drop_console: true, + drop_debugger: true, + } + } + })], + }, + + plugins: [ + new webpack.DllReferencePlugin({ + context: path.resolve(__dirname, "../../framework/src"), + manifest: require(path.resolve(frameworkPath, "vendor-manifest.json")), + sourceType: "umd2" + }), + new webpack.DllReferencePlugin({ + context: path.resolve(__dirname, "../../framework/src"), + manifest: require(path.resolve(frameworkPath, "app-manifest.json")), + sourceType: "umd2" + }), + ...(env === "release" ? [ + new webpack.DefinePlugin({ + "process.env": { + NODE_ENV: "'production'", + VERSION: JSON.stringify(require("./package.json").version) + } + }), + ] : [ + new webpack.DefinePlugin({ + "process.env": { + NODE_ENV: "'development'", + VERSION: JSON.stringify(require("./package.json").version) + } + }), + new CopyWebpackPlugin([{ + from: 'index.html', + to: distPath + }]), + ]) + ], + + devServer: { + public: "http://localhost:3100", + contentBase: frameworkPath, + + compress: true, + headers: { + "Access-Control-Allow-Origin": "*" + }, + host: "0.0.0.0", + port: 3100, + disableHostCheck: true, + historyApiFallback: true, + inline: true, + hot: false, + quiet: false, + stats: { + colors: true + }, + proxy: { + "/oauth2/": { + target: "http://localhost:3000", + secure: false + }, + "/database/": { + target: "http://localhost:3000", + secure: false + }, + "/restconf/": { + target: "http://localhost:3000", + secure: false + }, + "/help/": { + target: "http://localhost:3000", + secure: false + } + } + } + }]; +} |