diff options
Diffstat (limited to 'sdnr/wt/odlux/apps')
279 files changed, 20275 insertions, 0 deletions
diff --git a/sdnr/wt/odlux/apps/apiDemo/.babelrc b/sdnr/wt/odlux/apps/apiDemo/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/.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/apps/apiDemo/package.json b/sdnr/wt/odlux/apps/apiDemo/package.json new file mode 100644 index 000000000..d6f74278b --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/package.json @@ -0,0 +1,40 @@ +{ + "name": "@odlux/api-demo", + "version": "0.1.0", + "description": "A react based modular UI framework", + "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": { + "@odlux/framework": "*" + }, + "peerDependencies": { + "@types/react": "16.9.11", + "@types/react-dom": "16.9.4", + "@types/react-router-dom": "4.3.1", + "@material-ui/core": "4.6.1", + "@material-ui/icons": "4.5.1", + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "jquery": "3.3.1", + "react": "16.11.0", + "react-dom": "16.11.0", + "react-router-dom": "4.3.1" + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/apiDemo/pom.xml b/sdnr/wt/odlux/apps/apiDemo/pom.xml new file mode 100644 index 000000000..11bf306f2 --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/pom.xml @@ -0,0 +1,155 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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"> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> + <artifactId>sdnr-wt-odlux-app-apiDemo</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>sdnr-wt-odlux-app-apiDemo</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-provider</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.core</artifactId> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>org.osgi.compendium</artifactId> + </dependency> + </dependencies> + <build> + <sourceDirectory>src2/main/java</sourceDirectory> + <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>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src2/test/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.1</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>v10.16.3</nodeVersion> + <yarnVersion>v1.19.0</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package>org.onap.ccsdk.features.sdnr.wt.odlux.model.*,com.opensymphony.*</Import-Package> + <Private-Package/> + </instructions> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + <resource> + <directory>src2/main/resources</directory> + </resource> + <resource> + <directory>src2/test/resources</directory> + </resource> + </resources> + </build> +</project> diff --git a/sdnr/wt/odlux/apps/apiDemo/src/actions/modulesSuccess.ts b/sdnr/wt/odlux/apps/apiDemo/src/actions/modulesSuccess.ts new file mode 100644 index 000000000..12fd3fcaf --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/src/actions/modulesSuccess.ts @@ -0,0 +1,25 @@ +/** + * ============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 { ModuleResult } from '../models/module'; +export class ModulesRequestSuccess extends Action { + constructor(public result: ModuleResult) { + super(); + } +} +// error will be handled by the framework
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/apiDemo/src/handlers/apiDemoRootHandler.ts b/sdnr/wt/odlux/apps/apiDemo/src/handlers/apiDemoRootHandler.ts new file mode 100644 index 000000000..36688b3a5 --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/src/handlers/apiDemoRootHandler.ts @@ -0,0 +1,40 @@ +/** + * ============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 { combineActionHandler } from '../../../../framework/src/flux/middleware'; + +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import { moduleHandler, IModules } from './modulesHandler'; + +export interface IApiDemoStoreState { + modules: IModules +} + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + apiDemo: IApiDemoStoreState + } +} + +const actionHandlers = { + modules: moduleHandler +}; + +export const apiDemoRootHandler = combineActionHandler<IApiDemoStoreState>(actionHandlers); +export default apiDemoRootHandler; diff --git a/sdnr/wt/odlux/apps/apiDemo/src/handlers/modulesHandler.ts b/sdnr/wt/odlux/apps/apiDemo/src/handlers/modulesHandler.ts new file mode 100644 index 000000000..8777355db --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/src/handlers/modulesHandler.ts @@ -0,0 +1,33 @@ +/** + * ============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 { IActionHandler } from '../../../../framework/src/flux/action'; + +import { ModulesRequestSuccess } from '../actions/modulesSuccess'; +import { Module } from '../models/module'; + +export type IModules = Module[] + +const modulesInit: IModules = []; + +export const moduleHandler: IActionHandler<IModules> = (state = modulesInit, action) => { + if (action instanceof ModulesRequestSuccess) { + return action.result.modules.module; + } + + return state; +}; diff --git a/sdnr/wt/odlux/apps/apiDemo/src/index.html b/sdnr/wt/odlux/apps/apiDemo/src/index.html new file mode 100644 index 000000000..c01df6b13 --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/src/index.html @@ -0,0 +1,24 @@ +<!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>API Demo 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","apiDemo"], function (app) { + app("./app.tsx") + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/apiDemo/src/models/module.ts b/sdnr/wt/odlux/apps/apiDemo/src/models/module.ts new file mode 100644 index 000000000..abf86b76e --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/src/models/module.ts @@ -0,0 +1,28 @@ +/** + * ============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 Module = { + name: string; + revision: string; + namespace: string; +} + +export type ModuleResult = { + modules: { + module: Module[] + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/apiDemo/src/plugin.tsx b/sdnr/wt/odlux/apps/apiDemo/src/plugin.tsx new file mode 100644 index 000000000..6790ee929 --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/src/plugin.tsx @@ -0,0 +1,53 @@ +/** + * ============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 { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; + +import { faNewspaper } from '@fortawesome/free-solid-svg-icons'; + +import applicationManager from '../../../framework/src/services/applicationManager'; +import connect, { Connect } from '../../../framework/src/flux/connect'; +import { ApiAction } from '../../../framework/src/middleware/api'; // for RestConf + +import { apiDemoRootHandler } from './handlers/apiDemoRootHandler'; +import { ModulesRequestSuccess } from './actions/modulesSuccess'; +import { Module } from './models/module'; + +type AppProps = RouteComponentProps & Connect & { modules: Module[], requestModules: () => void }; + +const App = (props: AppProps ) => ( + <> + <button onClick={ props.requestModules }>Load Modules</button> + <ul>{ props.modules.map((mod, ind) => (<li key={ ind }>{ mod.name }</li>)) }</ul> + </> +); + +const FinalApp = withRouter(connect((state) => ({ + modules: state.apiDemo.modules +}), (dispatcher => ({ + requestModules: () => { dispatcher.dispatch(new ApiAction('restconf/modules', ModulesRequestSuccess, true)) } +})))(App)); + +applicationManager.registerApplication({ + name: "apiDemo", + icon: faNewspaper, + rootComponent: FinalApp, + rootActionHandler: apiDemoRootHandler, + menuEntry: "API Demo" +}); + diff --git a/sdnr/wt/odlux/apps/apiDemo/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java b/sdnr/wt/odlux/apps/apiDemo/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java new file mode 100644 index 000000000..1e882fc69 --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles; + +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class MyOdluxBundle extends OdluxBundle { + + @Override + public void initialize() { + super.initialize(); + } + + @Override + public void clean() { + super.clean(); + } + + @Override + public String getResourceFileContent(String filename) { + return super.getResourceFileContent(filename); + } + + @Override + public boolean hasResource(String filename) { + return super.hasResource(filename); + } + + @Override + public void setBundleName(String bundleName) { + super.setBundleName(bundleName); + } + + @Override + public void setLoader(OdluxBundleLoader loader) { + super.setLoader(loader); + } + + @Override + public String getBundleName() { + return super.getBundleName(); + } + + @Override + public OdluxBundleLoader getLoader() { + return super.getLoader(); + } + + public MyOdluxBundle() { + super(); + } +} diff --git a/sdnr/wt/odlux/apps/apiDemo/src2/main/resources/OSGI-INF/blueprint/blueprint.xml b/sdnr/wt/odlux/apps/apiDemo/src2/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..0877578e2 --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/src2/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,9 @@ +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <reference id="loadersvc" availability="mandatory" activation="eager" interface="org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader"/> + + <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle"> + <property name="loader" ref="loadersvc"/> + <property name="bundleName" value="apiDemo"/> + <property name="index" value="999"/> + </bean> +</blueprint>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/apiDemo/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java b/sdnr/wt/odlux/apps/apiDemo/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java new file mode 100644 index 000000000..e2ffb596b --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles.test; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.onap.ccsdk.features.sdnr.wt.odlux.OdluxBundleLoaderImpl; +import org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class TestBundleRes { + + @Test + public void test() { + OdluxBundleLoader loader = OdluxBundleLoaderImpl.getInstance(); + MyOdluxBundle b = new MyOdluxBundle(); + b.setLoader(loader); + b.setIndex(0); + b.setBundleName("abc"); + b.initialize(); + assertTrue(loader.getNumberOfBundles() == 1); + assertNotNull(b.getLoader()); + assertEquals("abc",b.getBundleName()); + assertTrue(b.hasResource("test.js")); + assertNotNull(b.getResourceFileContent("test.js")); + b.clean(); + assertTrue(loader.getNumberOfBundles() == 0); + } + +} diff --git a/sdnr/wt/odlux/apps/apiDemo/src2/test/resources/test.js b/sdnr/wt/odlux/apps/apiDemo/src2/test/resources/test.js new file mode 100644 index 000000000..b47fdc39f --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/src2/test/resources/test.js @@ -0,0 +1,5 @@ +asdac sad +as +d +sad + sadfa
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/apiDemo/tsconfig.json b/sdnr/wt/odlux/apps/apiDemo/tsconfig.json new file mode 100644 index 000000000..a66b5d828 --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": false, + "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": [ + "prop-types", + "react", + "react-dom" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/sdnr/wt/odlux/apps/apiDemo/webpack.config.js b/sdnr/wt/odlux/apps/apiDemo/webpack.config.js new file mode 100644 index 000000000..1c699e15b --- /dev/null +++ b/sdnr/wt/odlux/apps/apiDemo/webpack.config.js @@ -0,0 +1,139 @@ +/** + * 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: { + apiDemo: ["./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" + }] + }] + }, + 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 CopyWebpackPlugin([{ + // from: '../../../dist/**.*', + // to: path.resolve(__dirname, "dist") + // }]), + 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: { + "/restconf/**/*": { + target: "https://dlux.just-run.it", + secure: false, + changeOrigin: true + } + } + } + }]; +} diff --git a/sdnr/wt/odlux/apps/app-feature/pom.xml b/sdnr/wt/odlux/apps/app-feature/pom.xml new file mode 100644 index 000000000..dd67901ab --- /dev/null +++ b/sdnr/wt/odlux/apps/app-feature/pom.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- * ============LICENSE_START======================================================= + * ONAP : CCSDK.feature.sdnr.wt * ================================================================================ + * Copyright (C) 2018 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========================================================= --> +<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.wt</groupId> + <artifactId>sdnr-wt-odlux-apps-feature</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>feature</packaging> + <name>ccsdk-features-sdnr-wt :: ${project.artifactId} :: feature</name> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>single-feature-parent</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-connectApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-faultApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-helpApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-inventoryApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-mediatorApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-maintenanceApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-performanceHistoryApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-eventLogApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-configurationApp</artifactId> + <version>${project.version}</version> + </dependency> + </dependencies> +</project> diff --git a/sdnr/wt/odlux/apps/app-installer/pom.xml b/sdnr/wt/odlux/apps/app-installer/pom.xml new file mode 100755 index 000000000..20871a4d2 --- /dev/null +++ b/sdnr/wt/odlux/apps/app-installer/pom.xml @@ -0,0 +1,155 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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.wt</groupId> + <artifactId>sdnr-wt-odlux-apps-installer</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>pom</packaging> + <name>ccsdk-features-sdnr-wt :: ${project.artifactId}</name> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent-lite</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + + <properties> + <application.name>sdnr-wt-odlux-apps</application.name> + <include.transitive.dependencies>false</include.transitive.dependencies> + </properties> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + + <dependencies> + <dependency> + <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> + <artifactId>${application.name}-feature</artifactId> + <version>${project.version}</version> + <type>xml</type> + <classifier>features</classifier> + <exclusions> + <exclusion> + <groupId>*</groupId> + <artifactId>*</artifactId> + </exclusion> + </exclusions> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-apiDemo</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-connectApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-demoApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-faultApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-helpApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-inventoryApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-minimumApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-mediatorApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-maintenanceApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-performanceHistoryApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-eventLogApp</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-app-configurationApp</artifactId> + <version>${project.version}</version> + </dependency> + + </dependencies> + + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <executions> + <execution> + <id>maven-repo-zip</id> + <goals> + <goal>single</goal> + </goals> + <phase>package</phase> + <configuration> + <attach>true</attach> + <finalName>stage/${application.name}-${project.version}</finalName> + <descriptors> + <descriptor>src/assembly/assemble_mvnrepo_zip.xml</descriptor> + </descriptors> + <appendAssemblyId>true</appendAssemblyId> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>copy-nested-dependencies</id> + <goals> + <goal>copy-dependencies</goal> + </goals> + <phase>prepare-package</phase> + <configuration> + <transitive>true</transitive> + <outputDirectory>${project.build.directory}/assembly/system</outputDirectory> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>true</overWriteSnapshots> + <overWriteIfNewer>true</overWriteIfNewer> + <useRepositoryLayout>true</useRepositoryLayout> + <addParentPoms>false</addParentPoms> + <copyPom>false</copyPom> + <!--<includeArtifactIds>sdnr-wt-apigateway-provider,${application.name}</includeArtifactIds> --> + <!--<scope>provided</scope> --> + </configuration> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/sdnr/wt/odlux/apps/app-installer/src/assembly/assemble_mvnrepo_zip.xml b/sdnr/wt/odlux/apps/app-installer/src/assembly/assemble_mvnrepo_zip.xml new file mode 100644 index 000000000..c4eb9aa25 --- /dev/null +++ b/sdnr/wt/odlux/apps/app-installer/src/assembly/assemble_mvnrepo_zip.xml @@ -0,0 +1,49 @@ +<!-- + ============LICENSE_START======================================================= + ONAP : CCSDK + ================================================================================ + Copyright (C) 2017 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========================================================= + --> + +<!-- Defines how we build the .zip file which is our distribution. --> + +<assembly + xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>repo</id> + <formats> + <format>zip</format> + </formats> + + <!-- we want "system" and related files right at the root level + as this file is suppose to be unzip on top of a karaf + distro. --> + <includeBaseDirectory>false</includeBaseDirectory> + + <fileSets> + <fileSet> + <directory>target/assembly/</directory> + <outputDirectory>.</outputDirectory> + <excludes> + </excludes> + </fileSet> + </fileSets> + + + +</assembly> diff --git a/sdnr/wt/odlux/apps/configurationApp/.babelrc b/sdnr/wt/odlux/apps/configurationApp/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/.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/apps/configurationApp/package.json b/sdnr/wt/odlux/apps/configurationApp/package.json new file mode 100644 index 000000000..2985ba4fb --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/package.json @@ -0,0 +1,40 @@ +{ + "name": "@odlux/configuration-app", + "version": "0.1.0", + "description": "A react based modular UI for the configuration 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": { + "@odlux/framework": "*" + }, + "peerDependencies": { + "@types/react": "16.9.11", + "@types/react-dom": "16.9.4", + "@types/react-router-dom": "4.3.1", + "@material-ui/core": "4.6.1", + "@material-ui/icons": "4.5.1", + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "jquery": "3.3.1", + "react": "16.11.0", + "react-dom": "16.11.0", + "react-router-dom": "4.3.1" + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/pom.xml b/sdnr/wt/odlux/apps/configurationApp/pom.xml new file mode 100644 index 000000000..a4e067873 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/pom.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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"> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> + <artifactId>sdnr-wt-odlux-app-configurationApp</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>sdnr-wt-odlux-app-configurationApp</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-provider</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src2/main/java</sourceDirectory> + <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>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src2/test/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.1</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>v10.16.3</nodeVersion> + <yarnVersion>v1.19.0</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package>org.onap.ccsdk.features.sdnr.wt.odlux.model.*,com.opensymphony.*</Import-Package> + <Private-Package/> + </instructions> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + <resource> + <directory>src2/main/resources</directory> + </resource> + <resource> + <directory>src2/test/resources</directory> + </resource> + </resources> + </build> +</project> diff --git a/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts new file mode 100644 index 000000000..fc0665325 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts @@ -0,0 +1,380 @@ +import { Action } from '../../../../framework/src/flux/action'; +import { Dispatch } from '../../../../framework/src/flux/store'; +import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore"; +import { PushAction, ReplaceAction } from "../../../../framework/src/actions/navigationActions"; + +import { restService } from "../services/restServices"; +import { YangParser } from "../yang/yangParser"; +import { Module } from "../models/yang"; +import { ViewSpecification, ViewElement, isViewElementReference, isViewElementList, isViewElementObjectOrList } from "../models/uiModels"; +import { AddErrorInfoAction } from "../../../../framework/src/actions/errorActions"; + +export class EnableValueSelector extends Action { + constructor(public listSpecification: ViewSpecification, public listData: any[], public keyProperty: string, public onValueSelected : (value: any) => void ) { + super(); + } +} + +export class SetCollectingSelectionData extends Action { + constructor(public busy: boolean) { + super(); + } +} + +export class SetSelectedValue extends Action { + constructor(public value: any) { + super(); + } +} + +export class UpdateDeviceDescription extends Action { + constructor( public nodeId: string, public modules: { [name:string]: Module}, public views: ViewSpecification[]) { + super(); + } +} + +export class UpdatViewDescription extends Action { + constructor(public vPath: string, public view: ViewSpecification, public viewData: any, public displayAsList: boolean = false, public key?: string ) { + super(); + } +} + +export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState ) => { + const { configuration: { connectedNetworkElements : { rows }} } = getState(); + dispatch(new SetCollectingSelectionData(true)); + const networkElement = rows.find(r => r.nodeId === nodeId) || await restService.getMountedNetworkElementByMountId(nodeId); + if (!networkElement) { + console.error(new Error(`NetworkElement : [${nodeId}] does not exist.`)); + return dispatch(new UpdateDeviceDescription("", { }, [ ])); + } + + if (!networkElement.nodeDetails || !networkElement.nodeDetails.availableCapabilities) { + throw new Error(`NetworkElement : [${nodeId}] has no capabilities.`); + } + const parser = new YangParser(); + + const capParser = /^\(.*\?revision=(\d{4}-\d{2}-\d{2})\)(\S+)$/i; + for (let i = 0; i < networkElement.nodeDetails.availableCapabilities.length; ++i){ + const capRaw = networkElement.nodeDetails.availableCapabilities[i]; + const capMatch = capRaw && capParser.exec(capRaw); + try { + capMatch && await parser.addCapability(capMatch[2], capMatch[1]); + } catch (err) { + console.error(err); + } + } + + parser.postProcess(); + + console.log(parser.modules, parser.views) + + return dispatch(new UpdateDeviceDescription(nodeId, parser.modules, parser.views)); +} + +export const splitVPath = (vPath: string, vPathParser : RegExp): [string, string?][] => { + const pathParts: [string, string?][] = []; + let partMatch: RegExpExecArray | null; + if (vPath) do { + partMatch = vPathParser.exec(vPath); + if (partMatch) { + pathParts.push([partMatch[1], partMatch[2] || undefined]); + } + } while (partMatch) + return pathParts; +} + +const getReferencedDataList = async (refPath: string, dataPath: string, modules: { [name: string]: Module }, views: ViewSpecification[]) => { + const pathParts = splitVPath(refPath, /(?:(?:([^\/\:]+):)?([^\/]+))/g); // 1 = opt: namespace / 2 = property + let referencedModule = modules[pathParts[0][0]]; + + let dataMember: string; + let view: ViewSpecification; + let currentNS: string | null = null; + let dataUrls = [dataPath]; + let data: any; + + for (let i = 0; i < pathParts.length; ++i) { + const [pathPartNS, pathPart] = pathParts[i]; + const namespace = pathPartNS != null ? (currentNS = pathPartNS) : currentNS; + + const viewElement = i === 0 + ? views[0].elements[`${referencedModule.name}:${pathPart}`] + : view!.elements[`${pathPart}`] || view!.elements[`${namespace}:${pathPart}`]; + + if (!viewElement) throw new Error(`Could not find ${pathPart} in ${refPath}`); + if (i < pathParts.length - 1) { + if (!isViewElementObjectOrList(viewElement)) { + throw Error(`Module: [${referencedModule.name}].[${viewElement.label}]. Viewelement is not list or object.`); + } + view = views[+viewElement.viewId]; + const resultingDataUrls : string[] = []; + if (isViewElementList(viewElement)) { + for (let j = 0; j < dataUrls.length; ++j) { + const dataUrl = dataUrls[j]; + const restResult = (await restService.getConfigData(dataUrl)); + if (restResult.data == null || restResult.status < 200 || restResult.status > 299) { + const message = restResult.data && restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]["error-message"] || ""; + throw new Error(`Server Error. Status: [${restResult.status}]\n${message || restResult.message || ''}`); + } + + let dataRaw = restResult.data[dataMember!]; + dataRaw = dataRaw instanceof Array + ? dataRaw[0] + : dataRaw; + + data = dataRaw && dataRaw[viewElement.label] || []; + const keys: string[] = data.map((entry: { [key: string]: any } )=> entry[viewElement.key!]); + resultingDataUrls.push(...keys.map(key => `${dataUrl}/${viewElement.label.replace(/\//ig, "%2F")}/${key.replace(/\//ig, "%2F")}`)); + } + dataMember = viewElement.label; + } else { + // just a member, not a list + const pathSegment = (i === 0 + ? `/${referencedModule.name}:${viewElement.label.replace(/\//ig, "%2F")}` + : `/${viewElement.label.replace(/\//ig, "%2F")}`); + resultingDataUrls.push(...dataUrls.map(dataUrl => dataUrl + pathSegment)); + dataMember = viewElement.label; + } + dataUrls = resultingDataUrls; + } else { + data = []; + for (let j = 0; j < dataUrls.length; ++j) { + const dataUrl = dataUrls[j]; + const restResult = (await restService.getConfigData(dataUrl)); + if (restResult.data == null || restResult.status < 200 || restResult.status > 299) { + const message = restResult.data && restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]["error-message"] || ""; + throw new Error(`Server Error. Status: [${restResult.status}]\n${message || restResult.message || ''}`); + } + let dataRaw = restResult.data[dataMember!]; + dataRaw = dataRaw instanceof Array + ? dataRaw[0] + : dataRaw; + data.push(dataRaw); + } + // BUG UUID ist nicht in den elements enthalten !!!!!! + const key = viewElement && viewElement.label || pathPart; + return { + view: view!, + data: data, + key: key, + }; + } + } + return null; +} + +const resolveViewDescription = (defaultNS: string | null, vPath: string, view: ViewSpecification, viewData: any, displayAsList: boolean = false, key?: string): UpdatViewDescription =>{ + + // check if-feature | when | and resolve all references. + view = { ...view }; + view.elements = Object.keys(view.elements).reduce<{ [name: string]: ViewElement }>((acc, cur) => { + const elm = view.elements[cur]; + const key = defaultNS && cur.replace(new RegExp(`^${defaultNS}:`, "i"),"") || cur; + if (isViewElementReference(elm)) { + acc[key] = { ...(elm.ref(vPath) || elm), id: key }; + } else { + acc[key] = { ...elm, id: key }; + } + return acc; + }, {}); + return new UpdatViewDescription(vPath, view, viewData, displayAsList, key); +} + +export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => { + const pathParts = splitVPath(vPath, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key + const { configuration: { deviceDescription: { nodeId, modules, views } }, framework: { navigationState } } = getState(); + let dataPath = `/restconf/config/network-topology:network-topology/topology/topology-netconf/node/${nodeId}/yang-ext:mount`; + let viewSpecification: ViewSpecification = views[0]; + let viewElement: ViewElement; + + let dataMember: string; + let extractList: boolean = false; + + let currentNS : string | null = null; + let defaultNS : string | null = null; + + dispatch(new SetCollectingSelectionData(true)); + try { + for (let ind = 0; ind < pathParts.length; ++ind) { + const [property, key] = pathParts[ind]; + const namespaceInd = property && property.indexOf(":") || -1; + const namespace : string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS; + + if (ind === 0) { defaultNS = namespace }; + + viewElement = viewSpecification.elements[property]; + if (!viewElement) throw Error("Property [" + property + "] does not exist."); + + if (viewElement.isList && !key) { + if (pathParts.length - 1 > ind) { + dispatch(new SetCollectingSelectionData(false)); + throw new Error("No key for list [" + property + "]"); + } else if (vPath.endsWith("[]") && pathParts.length - 1 === ind) { + // empty key is used for new element + if (viewElement && "viewId" in viewElement) viewSpecification = views[+viewElement.viewId]; + const data = Object.keys(viewSpecification.elements).reduce<{ [name: string]: any }>((acc, cur) => { + const elm = viewSpecification.elements[cur]; + if (elm.default) { + acc[elm.id] = elm.default || "" + } + return acc; + }, {}); + return dispatch(resolveViewDescription(defaultNS, vPath, viewSpecification, data, false, isViewElementList(viewElement!) && viewElement.key || undefined)); + } + if (viewElement && isViewElementList(viewElement) && viewSpecification.parentView === "0") { + // check if there is a reference as key + const listSpecification = views[+viewElement.viewId]; + const keyElement = viewElement.key && listSpecification.elements[viewElement.key]; + if (keyElement && isViewElementReference(keyElement)) { + const refList = await getReferencedDataList(keyElement.referencePath, dataPath, modules, views); + if (!refList) { + throw new Error(`Could not find refList for [${keyElement.referencePath}].`); + } + if (!refList.key) { + throw new Error(`Key property not found for [${keyElement.referencePath}].`); + } + dispatch(new EnableValueSelector(refList.view, refList.data, refList.key, (refKey) => { + window.setTimeout(() => dispatch(new PushAction(`${vPath}[${refKey.replace(/\//ig, "%2F")}]`))); + })); + } else { + dispatch(new SetCollectingSelectionData(false)); + throw new Error("Found a list at root level of a module w/o a refenrece key."); + } + return; + } + extractList = true; + } else { + dataPath += `/${property}${key ? `/${key.replace(/\//ig, "%2F")}` : ""}`; + dataMember = namespace === defaultNS + ? viewElement.label + : `${namespace}:${viewElement.label}`; + extractList = false; + } + + if (viewElement && "viewId" in viewElement) viewSpecification = views[+viewElement.viewId]; + } + + let data: any = {}; + if (viewSpecification && viewSpecification.id !== "0") { + const restResult = (await restService.getConfigData(dataPath)); + if (!restResult.data) { + // special case: if this is a list without any response + if (extractList && restResult.status === 404) { + if (!isViewElementList(viewElement!)) { + throw new Error(`vPath: [${vPath}]. ViewElement has no key.`); + } + return dispatch(resolveViewDescription(defaultNS, vPath, viewSpecification, [], extractList, viewElement.key)); + } + throw new Error(`Did not get response from Server. Status: [${restResult.status}]`); + } else if (restResult.status < 200 || restResult.status > 299) { + const message = restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]["error-message"] || ""; + throw new Error(`Server Error. Status: [${restResult.status}]\n${message}`); + } else { + data = restResult.data[dataMember!]; // extract dataMember + } + + // extract the first element list[key] + data = data instanceof Array + ? data[0] + : data; + + // extract the list -> key: list + data = extractList + ? data[viewElement!.label] || [] // if the list is empty, it does not exist + : data; + } + + return dispatch(resolveViewDescription(defaultNS, vPath, viewSpecification, data, extractList, isViewElementList(viewElement!) && viewElement.key || undefined)); + // https://beta.just-run.it/#/configuration/Sim12600/core-model:network-element/ltp[LTP-MWPS-TTP-01] + // https://beta.just-run.it/#/configuration/Sim12600/core-model:network-element/ltp[LTP-MWPS-TTP-01]/lp + } catch (error) { + history.back(); + dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not process ${dataPath}` })); + dispatch(new SetCollectingSelectionData(false)); + } finally { + return; + } +} + +export const updateDataActionAsyncCreator = (vPath: string, data: any) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => { + const pathParts = splitVPath(vPath, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key + const { configuration: { deviceDescription: { nodeId, views } } } = getState(); + let dataPath = `/restconf/config/network-topology:network-topology/topology/topology-netconf/node/${nodeId}/yang-ext:mount`; + let viewSpecification: ViewSpecification = views[0]; + let viewElement: ViewElement; + let dataMember: string; + let embedList: boolean = false; + let isNew: string | false = false; + + let currentNS: string | null = null; + let defaultNS: string | null = null; + + dispatch(new SetCollectingSelectionData(true)); + try { + for (let ind = 0; ind < pathParts.length; ++ind) { + let [property, key] = pathParts[ind]; + const namespaceInd = property && property.indexOf(":") || -1; + const namespace: string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS; + + if (ind === 0) { defaultNS = namespace }; + viewElement = viewSpecification.elements[property]; + if (!viewElement) throw Error("Property [" + property + "] does not exist."); + + if (isViewElementList(viewElement) && !key) { + embedList = true; + if (viewElement && viewElement.isList && viewSpecification.parentView === "0") { + throw new Error("Found a list at root level of a module w/o a refenrece key."); + } + if (pathParts.length - 1 > ind) { + dispatch(new SetCollectingSelectionData(false)); + throw new Error("No key for list [" + property + "]"); + } else if (vPath.endsWith("[]") && pathParts.length - 1 === ind) { + // handle new element + key = viewElement.key && String(data[viewElement.key]) || ""; + isNew = key; + if (!key) { + dispatch(new SetCollectingSelectionData(false)); + throw new Error("No value for key [" + viewElement.key +"] in list [" + property + "]"); + } + } + } + + dataPath += `/${property}${key ? `/${key.replace(/\//ig, "%2F")}` : ""}`; + dataMember = viewElement.label; + embedList = false; + + if (viewElement && "viewId" in viewElement) { + viewSpecification = views[+viewElement.viewId]; + } + } + + // embed the list -> key: list + data = embedList + ? { [viewElement!.label]: data } + : data; + + // embed the first element list[key] + data = isNew + ? [data] + : data; + + // do not extract root member (0) + if (viewSpecification && viewSpecification.id !== "0") { + const updateResult = await restService.setConfigData(dataPath, { [dataMember!]: data }); // extractDataMember + if (updateResult.status < 200 || updateResult.status > 299) { + const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]["error-message"] || ""; + throw new Error(`Server Error. Status: [${updateResult.status}]\n${message || updateResult.message || ''}`); + } + } + + return isNew + ? dispatch(new ReplaceAction(`/configuration/${nodeId}/${vPath.replace(/\[\]$/i,`[${isNew}]`)}`)) // navigate to new element + : dispatch(resolveViewDescription(defaultNS, vPath, viewSpecification, data, embedList, isViewElementList(viewElement!) && viewElement.key || undefined)); + } catch (error) { + history.back(); + dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not change ${dataPath}` })); + dispatch(new SetCollectingSelectionData(false)); + } finally { + return; + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts new file mode 100644 index 000000000..04b63d39b --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts @@ -0,0 +1,29 @@ +import { combineActionHandler } from '../../../../framework/src/flux/middleware'; + +import { IConnectedNetworkElementsState, connectedNetworkElementsActionHandler } from './connectedNetworkElementsHandler'; +import { IDeviceDescriptionState, deviceDescriptionHandler } from "./deviceDescriptionHandler"; +import { IViewDescriptionState, viewDescriptionHandler } from "./viewDescriptionHandler"; +import { IValueSelectorState, valueSelectorHandler } from "./valueSelectorHandler"; + +interface IConfigurationAppStoreState { + connectedNetworkElements: IConnectedNetworkElementsState; // used for ne selection + deviceDescription: IDeviceDescriptionState; // contains ui and device descriptions + viewDescription: IViewDescriptionState; // contains current ui description + valueSelector: IValueSelectorState; +} + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + configuration: IConfigurationAppStoreState, + } +} + +const actionHandlers = { + connectedNetworkElements: connectedNetworkElementsActionHandler, + deviceDescription: deviceDescriptionHandler, + viewDescription: viewDescriptionHandler, + valueSelector: valueSelectorHandler, +}; + +export const configurationAppRootHandler = combineActionHandler<IConfigurationAppStoreState>(actionHandlers); +export default configurationAppRootHandler; diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts new file mode 100644 index 000000000..6a68242eb --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts @@ -0,0 +1,19 @@ +import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; +import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch'; + +import { NetworkElementConnection } from '../models/networkElementConnection'; + +export interface IConnectedNetworkElementsState extends IExternalTableState<NetworkElementConnection> { } + +// create eleactic search material data fetch handler +const connectedNetworkElementsSearchHandler = createSearchDataHandler<NetworkElementConnection>('network-element-connection', { status: "Connected" }); + +export const { + actionHandler: connectedNetworkElementsActionHandler, + createActions: createConnectedNetworkElementsActions, + createProperties: createConnectedNetworkElementsProperties, + reloadAction: connectedNetworkElementsReloadAction, + + // set value action, to change a value +} = createExternal<NetworkElementConnection>(connectedNetworkElementsSearchHandler, appState => appState.configuration.connectedNetworkElements); +
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts new file mode 100644 index 000000000..3cc27aa95 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts @@ -0,0 +1,30 @@ +import { Module } from "../models/yang"; +import { ViewSpecification } from "../models/uiModels"; +import { IActionHandler } from "../../../../framework/src/flux/action"; +import { UpdateDeviceDescription } from "../actions/deviceActions"; + +export interface IDeviceDescriptionState { + nodeId: string, + modules: { + [name: string]: Module + }, + views: ViewSpecification[], +} + +const deviceDescriptionStateInit: IDeviceDescriptionState = { + nodeId: "", + modules: {}, + views: [] +}; + +export const deviceDescriptionHandler: IActionHandler<IDeviceDescriptionState> = (state = deviceDescriptionStateInit, action) => { + if (action instanceof UpdateDeviceDescription) { + state = { + ...state, + nodeId: action.nodeId, + modules: action.modules, + views: action.views + }; + } + return state; +}; diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts new file mode 100644 index 000000000..c9e7dd2cc --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts @@ -0,0 +1,58 @@ +import { IActionHandler } from "../../../../framework/src/flux/action"; +import { ViewSpecification } from "../models/uiModels"; +import { EnableValueSelector, SetSelectedValue, UpdateDeviceDescription, SetCollectingSelectionData, UpdatViewDescription } from "../actions/deviceActions"; + +export interface IValueSelectorState { + collectingData: boolean; + keyProperty: string | undefined; + listSpecification: ViewSpecification | null; + listData: any[]; + onValueSelected: (value: any) => void; +} + +const nc = (val: React.SyntheticEvent) => { }; +const valueSelectorStateInit: IValueSelectorState = { + collectingData: false, + keyProperty: undefined, + listSpecification: null, + listData: [], + onValueSelected: nc, +}; + +export const valueSelectorHandler: IActionHandler<IValueSelectorState> = (state = valueSelectorStateInit, action) => { + if (action instanceof SetCollectingSelectionData) { + state = { + ...state, + collectingData: action.busy, + }; + } else if (action instanceof EnableValueSelector) { + state = { + ...state, + collectingData: false, + keyProperty: action.keyProperty, + listSpecification: action.listSpecification, + onValueSelected: action.onValueSelected, + listData: action.listData, + }; + } else if (action instanceof SetSelectedValue) { + state.keyProperty && state.onValueSelected(action.value[state.keyProperty]); + state = { + ...state, + collectingData: false, + keyProperty: undefined, + listSpecification: null, + onValueSelected: nc, + listData: [], + }; + } else if (action instanceof UpdateDeviceDescription || action instanceof UpdatViewDescription) { + state = { + ...state, + collectingData: false, + keyProperty: undefined, + listSpecification: null, + onValueSelected: nc, + listData: [], + }; + } + return state; +}; diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts new file mode 100644 index 000000000..48155ee1e --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts @@ -0,0 +1,42 @@ +import { IActionHandler } from "../../../../framework/src/flux/action"; + +import { UpdatViewDescription } from "../actions/deviceActions"; +import { ViewSpecification } from "../models/uiModels"; + +export interface IViewDescriptionState { + vPath: string | null; + keyProperty: string | undefined; + displayAsList: boolean; + viewSpecification: ViewSpecification; + viewData: any +} + +const viewDescriptionStateInit: IViewDescriptionState = { + vPath: null, + keyProperty: undefined, + displayAsList: false, + viewSpecification: { + id: "empty", + canEdit: false, + parentView: "", + name: "emplty", + language: "en-US", + title: "empty", + elements: {} + }, + viewData: null +}; + +export const viewDescriptionHandler: IActionHandler<IViewDescriptionState> = (state = viewDescriptionStateInit, action) => { + if (action instanceof UpdatViewDescription) { + state = { + ...state, + vPath: action.vPath, + keyProperty: action.key, + displayAsList: action.displayAsList, + viewSpecification: action.view, + viewData: action.viewData, + } + } + return state; +}; diff --git a/sdnr/wt/odlux/apps/configurationApp/src/index.html b/sdnr/wt/odlux/apps/configurationApp/src/index.html new file mode 100644 index 000000000..759b7b535 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/index.html @@ -0,0 +1,27 @@ +<!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>Configuration 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", "connectApp", "maintenanceApp", "configurationApp"], function (app, connectApp, maintenanceApp, configurationApp) { + connectApp.register(); + configurationApp.register(); + maintenanceApp.register(); + app("./app.tsx").runApplication(); + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts b/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts new file mode 100644 index 000000000..2575500a3 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts @@ -0,0 +1,19 @@ +export type NetworkElementConnection = { + id?: string; + nodeId: string; + host: string; + port: number; + username?: string; + password?: string; + isRequired?: boolean; + status?: "connected" | "mounted" | "unmounted" | "connecting" | "disconnected" | "idle"; + coreModelCapability?: string; + deviceType?: string; + nodeDetails?: { + availableCapabilities: string[]; + unavailableCapabilities: { + failureReason: string; + capability: string; + }[]; + } +} diff --git a/sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts b/sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts new file mode 100644 index 000000000..441d1281d --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts @@ -0,0 +1,142 @@ +export type ViewElementBase = { + "id": string; + "label": string; + "config": boolean; + "ifFeature"?: string; + "when"?: string; + "mandatory"?: boolean; + "description"?: string; + "isList"?: boolean; + "default"?: string; + "status"?: "current" | "deprecated" | "obsolete", + "reference"?: string, // https://tools.ietf.org/html/rfc7950#section-7.21.4 +} + +// https://tools.ietf.org/html/rfc7950#section-9.8 +export type ViewElementBinary = ViewElementBase & { + "uiType": "binary"; + "length"?: number; // number of octets +} + +// https://tools.ietf.org/html/rfc7950#section-9.7.4 +export type ViewElementBits = ViewElementBase & { + "uiType": "bits"; + "flags": { + [name: string]: number | undefined; // 0 - 4294967295 + } +} + +// https://tools.ietf.org/html/rfc7950#section-9 +export type ViewElementString = ViewElementBase & { + "uiType": "string"; + "pattern"?: string[]; + "length"?: string; + "invertMatch"?: true; +} + +// https://tools.ietf.org/html/rfc7950#section-9.3 +export type ViewElementNumber = ViewElementBase & { + "uiType": "number"; + "min"?: number; + "max"?: number; + "units"?: string; + "format"?: string; + "fDigits"?: number; +} + +// https://tools.ietf.org/html/rfc7950#section-9.5 +export type ViewElementBoolean = ViewElementBase & { + "uiType": "boolean"; + "trueValue"?: string; + "falseValue"?: string; +} + +// https://tools.ietf.org/html/rfc7950#section-9.6.4 +export type ViewElementSelection = ViewElementBase & { + "uiType": "selection"; + "multiSelect"?: boolean + "options": { + "key": string; + "value": string; + "description"?: string, + "status"?: "current" | "deprecated" | "obsolete", + "reference"?: string, + }[]; +} + +// is a list if isList is true ;-) +export type ViewElementObject = ViewElementBase & { + "uiType": "object"; + "isList"?: false; + "viewId": string; +} + +// Hint: read only lists do not need a key +export type ViewElementList = (ViewElementBase & { + "uiType": "object"; + "isList": true; + "viewId": string; + "key"?: string; +}); + +export type ViewElementReference = ViewElementBase & { + "uiType": "reference"; + "referencePath": string; + "ref": (currentPath: string) => ViewElement | null; +} + +export type ViewElement = + | ViewElementBits + | ViewElementBinary + | ViewElementString + | ViewElementNumber + | ViewElementBoolean + | ViewElementObject + | ViewElementList + | ViewElementSelection + | ViewElementReference; + +export const isViewElementString = (viewElement: ViewElement): viewElement is ViewElementString => { + return viewElement && viewElement.uiType === "string"; +} + +export const isViewElementNumber = (viewElement: ViewElement): viewElement is ViewElementNumber => { + return viewElement && viewElement.uiType === "number" ; +} + +export const isViewElementBoolean = (viewElement: ViewElement): viewElement is ViewElementBoolean => { + return viewElement && viewElement.uiType === "boolean"; +} + +export const isViewElementObject = (viewElement: ViewElement): viewElement is ViewElementObject => { + return viewElement && viewElement.uiType === "object" && viewElement.isList === false; +} + +export const isViewElementList = (viewElement: ViewElement): viewElement is ViewElementList => { + return viewElement && viewElement.uiType === "object" && viewElement.isList === true; +} + +export const isViewElementObjectOrList = (viewElement: ViewElement): viewElement is ViewElementObject | ViewElementList => { + return viewElement && viewElement.uiType === "object"; +} + +export const isViewElementSelection = (viewElement: ViewElement): viewElement is ViewElementSelection => { + return viewElement && viewElement.uiType === "selection"; +} + +export const isViewElementReference = (viewElement: ViewElement): viewElement is ViewElementReference => { + return viewElement && viewElement.uiType === "reference"; +} + +export type ViewSpecification = { + "id": string; + "name": string; + "title"?: string; + "parentView"?: string; + "language": string; + "ifFeature"?: string; + "when"?: string; + "uses"?: string[]; + "elements": { [name: string]: ViewElement }; + readonly "canEdit": boolean; +} diff --git a/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts b/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts new file mode 100644 index 000000000..57edf803f --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts @@ -0,0 +1,44 @@ +import { ViewElement, ViewSpecification } from "./uiModels"; + +export type Token = { + name: string; + value: string; + start: number; + end: number; +} + +export type Statement = { + key: string; + arg?: string; + sub?: Statement[]; +} + +export type Identity = { + id: string, + label: string, + base?: string, + description?: string, + reference?: string, + children?: Identity[], + values?: Identity[], +} + +export type Revision = { + description?: string, + reference?: string +}; + +export type Module = { + name: string; + namespace?: string; + prefix?: string; + identities: { [name: string]: Identity }; + revisions: { [version: string]: Revision } ; + imports: { [prefix: string]: string }; + features: { [feature: string]: { description?: string } }; + typedefs: { [type: string]: ViewElement }; + augments: { [path: string]: ViewSpecification[] }; + groupings: { [group: string]: ViewSpecification }; + views: { [view: string]: ViewSpecification }; + elements: { [view: string]: ViewElement }; +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx b/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx new file mode 100644 index 000000000..7fd3a97f9 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx @@ -0,0 +1,77 @@ +import * as React from "react"; +import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; + +import { faAdjust } from '@fortawesome/free-solid-svg-icons'; // select app icon + +import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect'; +import applicationManager from '../../../framework/src/services/applicationManager'; +import { IApplicationStoreState } from "../../../framework/src/store/applicationStore"; +import { configurationAppRootHandler } from "./handlers/configurationAppRootHandler"; +import { NetworkElementSelector } from "./views/networkElementSelector"; + +import ConfigurationApplication from "./views/configurationApplication"; +import { updateNodeIdAsyncActionCreator, updateViewActionAsyncCreator } from "./actions/deviceActions"; + +let currentNodeId: string | null | undefined = undefined; +let currentVirtualPath: string | null | undefined = undefined; +let lastUrl: string | undefined = undefined; + +const mapProps = (state: IApplicationStoreState) => ({ + // currentProblemsProperties: createCurrentProblemsProperties(state), +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + updateNodeId: (nodeId: string) => dispatcher.dispatch(updateNodeIdAsyncActionCreator(nodeId)), + updateView: (vPath: string) => dispatcher.dispatch(updateViewActionAsyncCreator(vPath)), +}); + +const ConfigurationApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ nodeId?: string, 0: string }> & Connect<typeof mapProps, typeof mapDisp>) => { + React.useEffect(() => { + return () => { + lastUrl = undefined; + currentNodeId = undefined; + currentVirtualPath = undefined; + } + },[]); + if (props.location.pathname !== lastUrl) { + // ensure the asynchronus update will only be called once per path + lastUrl = props.location.pathname; + window.setTimeout(async () => { + + // check if the nodeId has changed + if (currentNodeId !== props.match.params.nodeId) { + currentNodeId = props.match.params.nodeId || undefined; + currentVirtualPath = null; + currentNodeId && await props.updateNodeId(currentNodeId); + } + + if (currentVirtualPath !== props.match.params[0]) { + currentVirtualPath = props.match.params[0]; + await props.updateView(currentVirtualPath); + } + + }); + } + return ( + <ConfigurationApplication /> + ); +}); + +const App = withRouter((props: RouteComponentProps) => ( + <Switch> + <Route path={`${props.match.url}/:nodeId/*`} component={ ConfigurationApplicationRouteAdapter } /> + <Route path={`${props.match.url}/:nodeId`} component={ ConfigurationApplicationRouteAdapter } /> + <Route path={`${props.match.url}`} component={ NetworkElementSelector } /> + <Redirect to={`${props.match.url}`} /> + </Switch> +)); + +export function register() { + applicationManager.registerApplication({ + name: "configuration", + icon: faAdjust, + rootComponent: App, + rootActionHandler: configurationAppRootHandler, + menuEntry: "Configuration" + }); +} diff --git a/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts b/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts new file mode 100644 index 000000000..061be05ec --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts @@ -0,0 +1,38 @@ +import { requestRest, requestRestExt } from "../../../../framework/src/services/restService"; +import { convertPropertyNames, replaceHyphen } from "../../../../framework/src/utilities/yangHelper"; + +import { NetworkElementConnection } from "../models/networkElementConnection"; + +class RestService { + public async getMountedNetworkElementByMountId(nodeId: string): Promise<NetworkElementConnection | null> { + // const path = 'restconf/operational/network-topology:network-topology/topology/topology-netconf/node/' + nodeId; + // const connectedNetworkElement = await requestRest<NetworkElementConnection>(path, { method: "GET" }); + // return connectedNetworkElement || null; + + const path = "/restconf/operations/data-provider:read-network-element-connection-list"; + const body = { "input": { "filter": [{ "property": "node-id", "filtervalue": nodeId }], "sortorder": [], "pagination": { "size": 1, "page": 1 } } }; + const networkElementResult = await requestRest<{ output: { data: NetworkElementConnection[] } }>(path, { method: "POST", body: JSON.stringify(body) }); + return networkElementResult && networkElementResult.output && networkElementResult.output.data && + networkElementResult.output.data.map(obj => convertPropertyNames(obj, replaceHyphen))[0] || null; + } + + /** Reads the config data by restconf path. + * @param path The restconf path to be used for read. + * @returns The data. + */ + public getConfigData(path: string) { + return requestRestExt<{ [key: string]: any }>(path, { method: "GET" }); + } + + /** Updates or creates the config data by restconf path using data. + * @param path The restconf path to identify the note to update. + * @param data The data to be updated. + * @returns The written data. + */ + public setConfigData(path: string, data: any) { + return requestRestExt<{ [key: string]: any }>(path, { method: "PUT", body: JSON.stringify(data) }); + } + } + +export const restService = new RestService(); +export default restService;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts b/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts new file mode 100644 index 000000000..17a4e43a7 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts @@ -0,0 +1,25 @@ +type YangInfo = [string, (string | null | undefined)]; + +const cache: { [path: string]: string } = { + +}; + +class YangService { + + public async getCapability(capability: string, version?: string) { + const url = `/yang-schema/${capability}${version ? `/${version}` : ""}`; + + const cacheHit = cache[url]; + if (cacheHit) return cacheHit; + + const res = await fetch(url); + const yangFile = res.ok && await res.text(); + if (yangFile !== false && yangFile !== null) { + cache[url] = yangFile; + } + return yangFile; + } +} + +export const yangService = new YangService(); +export default yangService;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx new file mode 100644 index 000000000..24a4af8b2 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx @@ -0,0 +1,464 @@ +/** + * ============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 { RouteComponentProps, withRouter } from 'react-router-dom'; + +import { WithStyles, withStyles, createStyles, Theme } from '@material-ui/core/styles'; + +import connect, { IDispatcher, Connect } from "../../../../framework/src/flux/connect"; +import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore"; +import MaterialTable, { ColumnModel, ColumnType, MaterialTableCtorType } from "../../../../framework/src/components/material-table"; +import { Loader } from "../../../../framework/src/components/material-ui/loader"; + +import { SetSelectedValue, splitVPath, updateDataActionAsyncCreator } from "../actions/deviceActions"; +import { ViewSpecification, isViewElementString, isViewElementNumber, isViewElementBoolean, isViewElementObjectOrList, isViewElementSelection } from "../models/uiModels"; + +import Fab from '@material-ui/core/Fab'; +import AddIcon from '@material-ui/icons/Add'; +import RemoveIcon from '@material-ui/icons/RemoveCircleOutline'; +import SaveIcon from '@material-ui/icons/Save'; +import EditIcon from '@material-ui/icons/Edit'; +import Tooltip from "@material-ui/core/Tooltip"; +import TextField from "@material-ui/core/TextField"; +import FormControl from "@material-ui/core/FormControl"; +import IconButton from "@material-ui/core/IconButton"; +import Button from "@material-ui/core/Button"; +import InputAdornment from "@material-ui/core/InputAdornment"; +import InputLabel from "@material-ui/core/InputLabel"; +import Select from "@material-ui/core/Select"; +import MenuItem from "@material-ui/core/MenuItem"; +import Breadcrumbs from "@material-ui/core/Breadcrumbs"; +import Link from "@material-ui/core/Link"; +import FormHelperText from '@material-ui/core/FormHelperText'; + +const styles = (theme: Theme) => createStyles({ + header: { + "display": "flex", + "justifyContent": "space-between", + }, + leftButton: { + "justifyContent": "left" + }, + outer: { + "flex": "1", + "heigh": "100%", + "display": "flex", + "alignItems": "center", + "justifyContent": "center", + }, + inner: { + + }, + "icon": { + "marginRight": theme.spacing(0.5), + "width": 20, + "height": 20, + }, + "fab": { + "margin": theme.spacing(1), + }, + button: { + margin: 0, + padding: "6px 6px", + minWidth: 'unset' + }, + readOnly: { + '& label.Mui-focused': { + color: 'green', + }, + '& .MuiInput-underline:after': { + borderBottomColor: 'green', + }, + '& .MuiOutlinedInput-root': { + '& fieldset': { + borderColor: 'red', + }, + '&:hover fieldset': { + borderColor: 'yellow', + }, + '&.Mui-focused fieldset': { + borderColor: 'green', + }, + }, + }, +}); + +const mapProps = (state: IApplicationStoreState) => ({ + collectingData: state.configuration.valueSelector.collectingData, + listKeyProperty: state.configuration.valueSelector.keyProperty, + listSpecification: state.configuration.valueSelector.listSpecification, + listData: state.configuration.valueSelector.listData, + vPath: state.configuration.viewDescription.vPath, + nodeId: state.configuration.deviceDescription.nodeId, + viewData: state.configuration.viewDescription.viewData, + viewSpecification: state.configuration.viewDescription.viewSpecification, + displayAsList: state.configuration.viewDescription.displayAsList, + keyProperty: state.configuration.viewDescription.keyProperty, +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + onValueSelected: (value: any) => dispatcher.dispatch(new SetSelectedValue(value)), + onUpdateData: (vPath: string, data: any) => dispatcher.dispatch(updateDataActionAsyncCreator(vPath, data)), +}); + +const SelectElementTable = MaterialTable as MaterialTableCtorType<{ [key: string]: any }>; + +type ConfigurationApplicationComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDispatch> & WithStyles<typeof styles>; + +type ConfigurationApplicationComponentState = { + isNew: boolean; + editMode: boolean; + canEdit: boolean; + viewData: { [key: string]: any } | null; +} + +const OldProps = Symbol("OldProps"); +class ConfigurationApplicationComponent extends React.Component<ConfigurationApplicationComponentProps, ConfigurationApplicationComponentState> { + + /** + * + */ + constructor (props: ConfigurationApplicationComponentProps) { + super(props); + + this.state = { + isNew: false, + canEdit: false, + editMode: false, + viewData: null + } + } + + static getDerivedStateFromProps(nextProps: ConfigurationApplicationComponentProps, prevState: ConfigurationApplicationComponentState & { [OldProps]: ConfigurationApplicationComponentProps }) { + + if (!prevState || !prevState[OldProps] || (prevState[OldProps].viewData !== nextProps.viewData)) { + const isNew: boolean = nextProps.vPath?.endsWith("[]") || false; + const state = { + ...prevState, + isNew: isNew, + editMode: isNew, + viewData: nextProps.viewData || null, + [OldProps]: nextProps, + } + return state; + } + return null; + } + + private navigate = (path: string) => { + this.props.history.push(`${this.props.match.url}${path}`); + } + + private changeValueFor = (property: string, value: any) => { + this.setState({ + viewData: { + ...this.state.viewData, + [property]: value + } + }); + } + + private renderUIElement = (viewSpecification: ViewSpecification, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => { + const elements = viewSpecification.elements; + return ( + Object.keys(elements).sort((a, b) => { + const vsA = elements[a]; + const vsB = elements[b]; + if (keyProperty) { + // if (vsA.label === vsB.label) return 0; + if (vsA.label === keyProperty) return -1; + if (vsB.label === keyProperty) return +1; + } + + if (vsA.uiType === vsB.uiType) return 0; + if (vsA.uiType !== "object" && vsB.uiType !== "object") return 0; + if (vsA.uiType === "object") return +1; + return -1; + }).map(key => { + const uiElement = elements[key]; + const isKey = (uiElement.label === keyProperty); + const canEdit = editMode && (isNew || (uiElement.config && !isKey)); + if (isViewElementSelection(uiElement)) { + let error = "" + const value = String(viewData[uiElement.id]).toLowerCase(); + if (uiElement.mandatory && !!value) { + error = "Error"; + } + return (canEdit || viewData[uiElement.id] != null + ? (<FormControl key={uiElement.id} style={{ width: 485, marginLeft: 20, marginRight: 20 }}> + <InputLabel htmlFor={`select-${uiElement.id}`} >{uiElement.label}</InputLabel> + <Select + required={!!uiElement.mandatory} + error={!!error} + onChange={(e) => { this.changeValueFor(uiElement.id, e.target.value) }} + readOnly={!canEdit} + disabled={editMode && !canEdit} + value={(viewData[uiElement.id] || '').toString().toLowerCase()} + inputProps={{ + name: uiElement.id, + id: `select-${uiElement.id}`, + }} + > + {uiElement.options.map(option => (<MenuItem key={option.key} title={option.description} value={option.value}>{option.key}</MenuItem>))} + </Select> + <FormHelperText>{error}</FormHelperText> + </FormControl>) + : null + ); + } else if (isViewElementBoolean(uiElement)) { + let error = "" + const value = String(viewData[uiElement.id]).toLowerCase(); + if (uiElement.mandatory && value !== "true" && value !== "false") { + error = "Error"; + } + return (canEdit || viewData[uiElement.id] != null + ? (<FormControl key={uiElement.id} style={{ width: 485, marginLeft: 20, marginRight: 20 }}> + <InputLabel htmlFor={`select-${uiElement.id}`} >{uiElement.label}</InputLabel> + <Select + required={!!uiElement.mandatory} + error={!!error} + onChange={(e) => { this.changeValueFor(uiElement.id, e.target.value) }} + readOnly={!canEdit} + disabled={editMode && !canEdit} + value={value} + inputProps={{ + name: uiElement.id, + id: `select-${uiElement.id}`, + }} + > + <MenuItem value={'true'}>{uiElement.trueValue || 'True'}</MenuItem> + <MenuItem value={'false'}>{uiElement.falseValue || 'False'}</MenuItem> + + </Select> + <FormHelperText>{error}</FormHelperText> + </FormControl>) + : null + ); + } else if (isViewElementString(uiElement)) { + return ( + <Tooltip key={uiElement.id} title={uiElement.description || ''}> + <TextField InputProps={{ readOnly: !canEdit, disabled: editMode && !canEdit }} spellCheck={false} autoFocus margin="dense" + id={uiElement.id} label={isKey ? "🔑 " + uiElement.label : uiElement.label} type="text" value={viewData[uiElement.id] || ''} + style={{ width: 485, marginLeft: 20, marginRight: 20 }} + onChange={(e) => { this.changeValueFor(uiElement.id, e.target.value) }} + /> + </Tooltip> + ); + } else if (isViewElementNumber(uiElement)) { + return ( + <Tooltip key={uiElement.id} title={uiElement.description || ''}> + <TextField InputProps={{ readOnly: !canEdit, disabled: editMode && !canEdit, startAdornment: uiElement.units != null ? <InputAdornment position="start">{uiElement.units}</InputAdornment> : undefined }} spellCheck={false} autoFocus margin="dense" + id={uiElement.id} label={uiElement.label} type="text" value={viewData[uiElement.id] == null ? '' : viewData[uiElement.id]} + style={{ width: 485, marginLeft: 20, marginRight: 20 }} + onChange={(e) => { this.changeValueFor(uiElement.id, e.target.value) }} + /> + </Tooltip> + ); + } else if (isViewElementObjectOrList(uiElement)) { + return ( + <FormControl key={uiElement.id} style={{ width: 485, marginLeft: 20, marginRight: 20 }}> + <Tooltip title={uiElement.description || ''}> + <Button className={this.props.classes.leftButton} color="secondary" disabled={this.state.editMode} onClick={() => { + this.navigate(`/${uiElement.id}`); + }}>{uiElement.label}</Button> + </Tooltip> + </FormControl> + ); + } else { + if (process.env.NODE_ENV !== "production") { + console.error(`Unknown type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`) + } + return null; + } + }) + ); + }; + + private renderUIElementList(listSpecification: ViewSpecification, listKeyProperty: string, listData: { [key: string]: any }[]) { + const listElements = listSpecification.elements; + + const navigate = (path: string) => { + this.props.history.push(`${this.props.match.url}${path}`); + }; + + const addNewElementAction = { + icon: AddIcon, tooltip: 'Add', onClick: () => { + navigate("[]"); // empty key means new element + } + }; + + const { classes } = this.props; + + return ( + <SelectElementTable idProperty={listKeyProperty} rows={listData} customActionButtons={[addNewElementAction]} columns={ + Object.keys(listElements).reduce<ColumnModel<{ [key: string]: any }>[]>((acc, cur) => { + const elm = listElements[cur]; + if (elm.uiType !== "object" && listData.every(entry => entry[elm.label] != null)) { + if (elm.label !== listKeyProperty) { + acc.push({ property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text }); + } else { + acc.unshift({ property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text }); + } + } + return acc; + }, []).concat([{ + property: "Actions", disableFilter: true, disableSorting: true, type: ColumnType.custom, customControl: (row => { + return ( + <Tooltip title={"Remove"} > + <IconButton className={classes.button} onClick={event => { + + }} > + <RemoveIcon /> + </IconButton> + </Tooltip> + ) + }) + }]) + } onHandleClick={(ev, row) => { + ev.preventDefault(); + navigate(`[${row[listKeyProperty]}]`); + }} ></SelectElementTable> + ); + } + + private renderBreadCrumps() { + const { editMode } = this.state; + const { viewSpecification, displayAsList } = this.props; + const { vPath, match: { url, path }, nodeId } = this.props; + const pathParts = splitVPath(vPath!, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key + let lastPath = `/configuration`; + let basePath = `/configuration/${nodeId}`; + return ( + <div className={this.props.classes.header}> + <div> + <Breadcrumbs aria-label="breadcrumb"> + <Link color="inherit" href="#" onClick={(ev: React.MouseEvent<HTMLElement>) => { + ev.preventDefault(); + this.props.history.push(lastPath); + }}>Back</Link> + <Link color="inherit" href="#" onClick={(ev: React.MouseEvent<HTMLElement>) => { + ev.preventDefault(); + this.props.history.push(`/configuration/${nodeId}`); + }}><span>{nodeId}</span></Link> + { + pathParts.map(([prop, key], ind) => { + const path = `${basePath}/${prop}`; + const keyPath = key && `${basePath}/${prop}[${key}]`; + const ret = ( + <span key={ind}> + <Link color="inherit" href="#" onClick={(ev: React.MouseEvent<HTMLElement>) => { + ev.preventDefault(); + this.props.history.push(path); + }}><span>{prop.replace(/^[^:]+:/, "")}</span></Link> + { + keyPath && <Link color="inherit" href="#" onClick={(ev: React.MouseEvent<HTMLElement>) => { + ev.preventDefault(); + this.props.history.push(keyPath); + }}>{`[${key}]`}</Link> || null + } + </span> + ); + lastPath = basePath; + basePath = keyPath || path; + return ret; + }) + } + </Breadcrumbs> + </div> + { /* do not show edit if this is a list or it can't be edited */ + !displayAsList && viewSpecification.canEdit && (<div> + <Fab color="secondary" aria-label="edit" className={this.props.classes.fab} onClick={() => { + if (this.state.editMode) { + this.props.onUpdateData(this.props.vPath!, this.state.viewData); + } + this.setState({ editMode: !editMode }); + }}> + {editMode + ? <SaveIcon /> + : <EditIcon /> + } + </Fab> + </div> || null) + } + </div> + ); + } + + private renderValueSelector() { + const { listKeyProperty, listSpecification, listData, onValueSelected } = this.props; + if (!listKeyProperty || !listSpecification) { + throw new Error("ListKex ot view not specified."); + } + + return ( + <div> + <SelectElementTable idProperty={listKeyProperty} rows={listData} columns={ + Object.keys(listSpecification.elements).reduce<ColumnModel<{ [key: string]: any }>[]>((acc, cur) => { + const elm = listSpecification.elements[cur]; + if (elm.uiType !== "object" && listData.every(entry => entry[elm.label] != null)) { + if (elm.label !== listKeyProperty) { + acc.push({ property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text }); + } else { + acc.unshift({ property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text }); + } + } + return acc; + }, []) + } onHandleClick={(ev, row) => { ev.preventDefault(); onValueSelected(row); }} ></SelectElementTable> + </div> + ); + } + + private renderValueEditor() { + const { keyProperty, displayAsList, viewSpecification } = this.props; + const { viewData, editMode, isNew } = this.state; + + return ( + <div> + { this.renderBreadCrumps() } + { displayAsList && viewData instanceof Array + ? this.renderUIElementList(viewSpecification, keyProperty!, viewData) + : this.renderUIElement(viewSpecification, viewData!, keyProperty, editMode, isNew) + } + </div > + ); + } + + private renderCollectingData() { + return ( + <div className={this.props.classes.outer}> + <div className={this.props.classes.inner}> + <Loader /> + <h3>Collecting Data ...</h3> + </div> + </div> + ); + } + + render() { + return this.props.collectingData || !this.state.viewData + ? this.renderCollectingData() + : this.props.listSpecification + ? this.renderValueSelector() + : this.renderValueEditor(); + } +} + +export const ConfigurationApplication = withStyles(styles)(withRouter(connect(mapProps, mapDispatch)(ConfigurationApplicationComponent))); +export default ConfigurationApplication;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx new file mode 100644 index 000000000..6fd5c8cf0 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx @@ -0,0 +1,47 @@ +import * as React from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; + +import connect, { IDispatcher, Connect } from "../../../../framework/src/flux/connect"; +import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore"; +import { MaterialTable, MaterialTableCtorType, ColumnType } from "../../../../framework/src/components/material-table"; +import { createConnectedNetworkElementsProperties, createConnectedNetworkElementsActions } from "../../../configurationApp/src/handlers/connectedNetworkElementsHandler"; + +import { NetworkElementConnection } from "../models/networkElementConnection"; +import { Tooltip, Button, IconButton } from "@material-ui/core"; + +const mapProps = (state: IApplicationStoreState) => ({ + connectedNetworkElementsProperties: createConnectedNetworkElementsProperties(state), +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + connectedNetworkElementsActions: createConnectedNetworkElementsActions(dispatcher.dispatch), +}); + +const ConnectedElementTable = MaterialTable as MaterialTableCtorType<NetworkElementConnection>; + +type NetworkElementSelectorComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDispatch>; + +class NetworkElementSelectorComponent extends React.Component<NetworkElementSelectorComponentProps> { + + componentDidMount() { + this.props.connectedNetworkElementsActions.onRefresh(); + } + + render() { + return ( + <ConnectedElementTable onHandleClick={(e, row) => { this.props.history.push(`${ this.props.match.path }/${row.nodeId}`) }} columns={[ + { property: "nodeId", title: "Name", type: ColumnType.text }, + { property: "isRequired", title: "Required ?", type: ColumnType.boolean }, + { property: "host", title: "Host", type: ColumnType.text }, + { property: "port", title: "Port", type: ColumnType.numeric }, + { property: "coreModelCapability", title: "Core Model", type: ColumnType.text }, + { property: "deviceType", title: "Type", type: ColumnType.text }, + ]} idProperty="id" {...this.props.connectedNetworkElementsActions} {...this.props.connectedNetworkElementsProperties} asynchronus > + </ConnectedElementTable> + ); + } +} + +export const NetworkElementSelector = withRouter(connect(mapProps, mapDispatch)(NetworkElementSelectorComponent)); +export default NetworkElementSelector; + diff --git a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts new file mode 100644 index 000000000..c7ab5e4a3 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts @@ -0,0 +1,1099 @@ + +import { Token, Statement, Module, Identity } from "../models/yang"; +import { ViewSpecification, ViewElement, isViewElementObjectOrList, ViewElementBase, isViewElementReference } from "../models/uiModels"; +import { yangService } from "../services/yangService"; + +export const splitVPath = (vPath: string, vPathParser: RegExp): RegExpMatchArray[] => { + const pathParts: RegExpMatchArray[] = []; + let partMatch: RegExpExecArray | null; + if (vPath) do { + partMatch = vPathParser.exec(vPath); + if (partMatch) { + pathParts.push(partMatch); + } + } while (partMatch) + return pathParts; +} + +class YangLexer { + + private pos: number = 0; + private buf: string = ""; + + constructor(input: string) { + this.pos = 0; + this.buf = input; + } + + private _optable: { [key: string]: string } = { + ';': 'SEMI', + '{': 'L_BRACE', + '}': 'R_BRACE', + }; + + private _isNewline(char: string): boolean { + return char === '\r' || char === '\n'; + } + + private _isWhitespace(char: string): boolean { + return char === ' ' || char === '\t' || this._isNewline(char); + } + + private _isDigit(char: string): boolean { + return char >= '0' && char <= '9'; + } + + private _isAlpha(char: string): boolean { + return (char >= 'a' && char <= 'z') || + (char >= 'A' && char <= 'Z') + } + + private _isAlphanum(char: string): boolean { + return this._isAlpha(char) || this._isDigit(char) || + char === '_' || char === '-' || char === '.'; + } + + private _skipNontokens() { + while (this.pos < this.buf.length) { + const char = this.buf.charAt(this.pos); + if (this._isWhitespace(char)) { + this.pos++; + } else { + break; + } + } + } + + private _processString(terminator: string | null): Token { + // this.pos points at the opening quote. Find the ending quote. + let end_index = this.pos + 1; + while (end_index < this.buf.length) { + const char = this.buf.charAt(end_index); + if (char === "\\") { + end_index += 2; + continue; + }; + if (terminator === null && (this._isWhitespace(char) || this._optable[char] !== undefined) || char === terminator) { + break; + } + end_index++; + } + + if (end_index >= this.buf.length) { + throw Error('Unterminated quote at ' + this.pos); + } else { + const start = this.pos + (terminator ? 1 : 0); + const end = end_index; + const tok = { + name: 'STRING', + value: this.buf.substring(start, end), + start, + end + }; + this.pos = terminator ? end + 1 : end; + return tok; + } + } + + private _processIdentifier(): Token { + let endpos = this.pos + 1; + while (endpos < this.buf.length && + this._isAlphanum(this.buf.charAt(endpos))) { + endpos++; + } + + const tok = { + name: 'IDENTIFIER', + value: this.buf.substring(this.pos, endpos), + start: this.pos, + end: endpos + }; + this.pos = endpos; + return tok; + } + + private _processNumber(): Token { + let endpos = this.pos + 1; + while (endpos < this.buf.length && + this._isDigit(this.buf.charAt(endpos))) { + endpos++; + } + + const tok = { + name: 'NUMBER', + value: this.buf.substring(this.pos, endpos), + start: this.pos, + end: endpos + }; + this.pos = endpos; + return tok; + } + + private _processLineComment() { + var endpos = this.pos + 2; + // Skip until the end of the line + while (endpos < this.buf.length && !this._isNewline(this.buf.charAt(endpos))) { + endpos++; + } + this.pos = endpos + 1; + } + + private _processBlockComment() { + var endpos = this.pos + 2; + // Skip until the end of the line + while (endpos < this.buf.length && !((this.buf.charAt(endpos) === "/" && this.buf.charAt(endpos - 1) === "*"))) { + endpos++; + } + this.pos = endpos + 1; + } + + public tokenize(): Token[] { + const result: Token[] = []; + this._skipNontokens(); + while (this.pos < this.buf.length) { + + const char = this.buf.charAt(this.pos); + const op = this._optable[char]; + + if (op !== undefined) { + result.push({ name: op, value: char, start: this.pos, end: ++this.pos }); + } else if (this._isAlpha(char)) { + result.push(this._processIdentifier()); + this._skipNontokens(); + const peekChar = this.buf.charAt(this.pos); + if (this._optable[peekChar] === undefined) { + result.push((peekChar !== "'" && peekChar !== '"') + ? this._processString(null) + : this._processString(peekChar)); + } + } else if (char === '/' && this.buf.charAt(this.pos + 1) === "/") { + this._processLineComment(); + } else if (char === '/' && this.buf.charAt(this.pos + 1) === "*") { + this._processBlockComment(); + } else { + throw Error('Token error at ' + this.pos + " " + this.buf[this.pos]); + } + this._skipNontokens(); + } + return result; + } + + public tokenize2(): Statement { + let stack: Statement[] = [{ key: "ROOT", sub: [] }]; + let current: Statement | null = null; + + this._skipNontokens(); + while (this.pos < this.buf.length) { + + const char = this.buf.charAt(this.pos); + const op = this._optable[char]; + + if (op !== undefined) { + if (op === "L_BRACE") { + current && stack.unshift(current); + current = null; + } else if (op === "R_BRACE") { + current = stack.shift() || null; + } + this.pos++; + } else if (this._isAlpha(char)) { + const key = this._processIdentifier().value; + this._skipNontokens(); + let peekChar = this.buf.charAt(this.pos); + let arg = undefined; + if (this._optable[peekChar] === undefined) { + arg = (peekChar === '"' || peekChar === "'") + ? this._processString(peekChar).value + : this._processString(null).value; + } + do { + this._skipNontokens(); + peekChar = this.buf.charAt(this.pos); + if (peekChar !== "+") break; + this.pos++; + this._skipNontokens(); + peekChar = this.buf.charAt(this.pos); + arg += (peekChar === '"' || peekChar === "'") + ? this._processString(peekChar).value + : this._processString(null).value; + } while (true); + current = { key, arg, sub: [] }; + stack[0].sub!.push(current); + } else if (char === '/' && this.buf.charAt(this.pos + 1) === "/") { + this._processLineComment(); + } else if (char === '/' && this.buf.charAt(this.pos + 1) === "*") { + this._processBlockComment(); + } else { + throw Error('Token error at ' + this.pos + " " + this.buf.slice(this.pos - 10, this.pos + 10)); + } + this._skipNontokens(); + } + if (stack[0].key !== "ROOT" || !stack[0].sub![0]) { + throw new Error("Internal Perser Error"); + } + return stack[0].sub![0]; + } +} + +export class YangParser { + private _groupingsToResolve: (() => void)[] = []; + private _identityToResolve: (() => void)[] = []; + + private _modules: { [name: string]: Module } = {}; + private _views: ViewSpecification[] = [{ + id: "0", + name: "root", + language: "en-US", + canEdit: false, + parentView: "0", + title: "root", + elements: {}, + }]; + + constructor() { + + } + + public get modules() { + return this._modules; + } + + public get views() { + return this._views; + } + + public async addCapability(capability: string, version?: string) { + // do not add twice + if (this._modules[capability]) { + return; + } + + const data = await yangService.getCapability(capability, version); + if (!data) { + throw new Error(`Could not load yang file for ${capability}.`); + } + + const rootStatement = new YangLexer(data).tokenize2(); + + if (rootStatement.key !== "module") { + throw new Error(`Root element of ${capability} is not a module.`); + } + if (rootStatement.arg !== capability) { + throw new Error(`Root element capability ${rootStatement.arg} does not requested ${capability}.`); + } + + const module = this._modules[capability] = { + name: rootStatement.arg, + revisions: {}, + imports: {}, + features: {}, + identities: {}, + augments: {}, + groupings: {}, + typedefs: {}, + views: {}, + elements: {} + }; + + await this.handleModule(module, rootStatement, capability); + } + + private async handleModule(module: Module, rootStatement: Statement, capability: string) { + + // extract namespace && prefix + module.namespace = this.extractValue(rootStatement, "namespace"); + module.prefix = this.extractValue(rootStatement, "prefix"); + if (module.prefix) { + module.imports[module.prefix] = capability; + } + + // extract revisions + const revisions = this.extractNodes(rootStatement, "revision"); + module.revisions = { + ...module.revisions, + ...revisions.reduce<{ [version: string]: { }}>((acc, version) => { + if (!version.arg) { + throw new Error(`Module [${module.name}] has a version w/o version number.`); + } + const description = this.extractValue(version, "description"); + const reference = this.extractValue(version,"reference"); + acc[version.arg] = { + description, + reference, + }; + return acc; + }, {}) + }; + + // extract features + const features = this.extractNodes(rootStatement, "feature"); + module.features = { + ...module.features, + ...features.reduce<{ [version: string]: {} }>((acc, feature) => { + if (!feature.arg) { + throw new Error(`Module [${module.name}] has a feature w/o name.`); + } + const description = this.extractValue(feature, "description"); + acc[feature.arg] = { + description, + }; + return acc; + }, {}) + }; + + // extract imports + const imports = this.extractNodes(rootStatement, "import"); + module.imports = { + ...module.imports, + ...imports.reduce < { [key: string]: string }>((acc, imp) => { + const prefix = imp.sub && imp.sub.filter(s => s.key === "prefix"); + if (!imp.arg) { + throw new Error(`Module [${module.name}] has an import with neither name nor prefix.`); + } + acc[prefix && prefix.length === 1 && prefix[0].arg || imp.arg] = imp.arg; + return acc; + }, {}) + }; + + // import all required files + if (imports) for (let ind = 0; ind < imports.length; ++ind) { + await this.addCapability(imports[ind].arg!); + } + + this.extractTypeDefinitions(rootStatement, module, ""); + + this.extractIdentites(rootStatement, 0, module, ""); + + const groupings = this.extractGroupings(rootStatement, 0, module, ""); + this._views.push(...groupings); + + const augments = this.extractAugments(rootStatement, 0, module, ""); + this._views.push(...augments); + + // the default for config on module level is config = true; + const [currentView, subViews] = this.extractSubViews(rootStatement, 0, module, ""); + this._views.push(currentView, ...subViews); + + // create the root elements for this module + module.elements = currentView.elements; + Object.keys(module.elements).forEach(key => { + const viewElement = module.elements[key]; + if (!isViewElementObjectOrList(viewElement)) { + throw new Error(`Module: [${module}]. Only List or Object allowed on root level.`); + } + const viewIdIndex = Number(viewElement.viewId); + module.views[key] = this._views[viewIdIndex]; + this._views[0].elements[key] = module.elements[key]; + }); + return module; + } + + public postProcess() { + // process all groupings + // execute all post processes like resolving in propper order + this._groupingsToResolve.forEach(cb => { + try { cb(); } catch (error) { + console.warn(`Error resolving: [${error.message}]`); + } + }); + + // process all augmentations + Object.keys(this.modules).forEach(modKey => { + const module = this.modules[modKey]; + Object.keys(module.augments).forEach(augKey => { + const augments = module.augments[augKey]; + const viewSpec = this.resolveView(augKey); + if (!viewSpec) console.warn(`Could not find view to augment [${augKey}] in [${module.name}].`); + if (augments && viewSpec) { + augments.forEach(augment => Object.keys(augment.elements).forEach(key => { + const elm = augment.elements[key]; + viewSpec.elements[key] = { + ...augment.elements[key], + when: elm.when ? `(${augment.when}) and (${elm.when})` : augment.when, + ifFeature: elm.ifFeature ? `(${augment.ifFeature}) and (${elm.ifFeature})` : augment.ifFeature, + }; + })); + } + }); + }); + + // process Identities + const traverseIdentity = (identities : Identity[]) => { + const result: Identity[] = []; + for (let identity of identities) { + if (identity.children && identity.children.length > 0) { + result.push(...traverseIdentity(identity.children)); + } else { + result.push(identity); + } + } + return result; + } + + + const baseIdentites: Identity[] = []; + Object.keys(this.modules).forEach(modKey => { + const module = this.modules[modKey]; + Object.keys(module.identities).forEach(idKey => { + const identity = module.identities[idKey]; + if (identity.base != null) { + const base = this.resolveIdentity(identity.base, module); + base.children?.push(identity); + } else { + baseIdentites.push(identity); + } + }); + }); + baseIdentites.forEach(identity => { + identity.values = identity.children && traverseIdentity(identity.children) || []; + }); + + this._identityToResolve.forEach(cb => { + try { cb(); } catch (error) { + console.warn(error.message); + } + }); + }; + + + private _nextId = 1; + private get nextId() { + return this._nextId++; + } + + private extractNodes(statement: Statement, key: string): Statement[] { + return statement.sub && statement.sub.filter(s => s.key === key) || []; + } + + private extractValue(statement: Statement, key: string): string | undefined; + private extractValue(statement: Statement, key: string, parser: RegExp): RegExpExecArray | undefined; + private extractValue(statement: Statement, key: string, parser?: RegExp): string | RegExpExecArray | undefined { + const typeNodes = this.extractNodes(statement, key); + const rawValue = typeNodes.length > 0 && typeNodes[0].arg || undefined; + return parser + ? rawValue && parser.exec(rawValue) || undefined + : rawValue; + } + + private extractTypeDefinitions(statement: Statement, module: Module, currentPath: string): void { + const typedefs = this.extractNodes(statement, "typedef"); + typedefs && typedefs.forEach(def => { + if (! def.arg) { + throw new Error(`Module: [${module.name}]. Found typefed without name.`); + } + module.typedefs[def.arg] = this.getViewElement(def, module, 0, currentPath, false); + }); + } + + /** Handles Goupings like named Container */ + private extractGroupings(statement: Statement, parentId: number, module: Module, currentPath: string): ViewSpecification[] { + const subViews: ViewSpecification[] = []; + const groupings = this.extractNodes(statement, "grouping"); + if (groupings && groupings.length > 0) { + subViews.push(...groupings.reduce<ViewSpecification[]>((acc, cur) => { + if (!cur.arg) { + throw new Error(`Module: [${module.name}][${currentPath}]. Found grouping without name.`); + } + const grouping = cur.arg; + + // the default for config on module level is config = true; + const [currentView, subViews] = this.extractSubViews(cur, parentId, module, currentPath); + grouping && (module.groupings[grouping] = currentView); + acc.push(currentView, ...subViews); + return acc; + }, [])); + } + + return subViews; + } + + /** Handles Augmants also like named Container */ + private extractAugments(statement: Statement, parentId: number, module: Module, currentPath: string): ViewSpecification[] { + const subViews: ViewSpecification[] = []; + const augments = this.extractNodes(statement, "augment"); + if (augments && augments.length > 0) { + subViews.push(...augments.reduce<ViewSpecification[]>((acc, cur) => { + if (!cur.arg) { + throw new Error(`Module: [${module.name}][${currentPath}]. Found augment without path.`); + } + const augment = this.resolveReferencePath(cur.arg, module); + + // the default for config on module level is config = true; + const [currentView, subViews] = this.extractSubViews(cur, parentId, module, currentPath); + if (augment) { + module.augments[augment] = module.augments[augment] || []; + module.augments[augment].push(currentView); + } + acc.push(currentView, ...subViews); + return acc; + }, [])); + } + + return subViews; + } + + /** Handles Identities */ + private extractIdentites(statement: Statement, parentId: number, module: Module, currentPath: string) { + const identities = this.extractNodes(statement, "identity"); + module.identities = identities.reduce<{ [name: string]: Identity }>((acc, cur) => { + if (!cur.arg) { + throw new Error(`Module: [${module.name}][${currentPath}]. Found identiy without name.`); + } + acc[cur.arg] = { + id: `${module.name}:${cur.arg}`, + label: cur.arg, + base: this.extractValue(cur, "base"), + description: this.extractValue(cur, "description"), + reference: this.extractValue(cur, "reference"), + children: [] + } + return acc; + }, {}); + } + + private extractSubViews(statement: Statement, parentId: number, module: Module, currentPath: string): [ViewSpecification, ViewSpecification[]] { + const subViews: ViewSpecification[] = []; + const currentId = this.nextId; + let elements: ViewElement[] = []; + + const configValue = this.extractValue(statement, "config"); + const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false"; + + // extract conditions + const ifFeature = this.extractValue(statement, "if-feature"); + const whenCondition = this.extractValue(statement, "when"); + + // extract all container + const container = this.extractNodes(statement, "container"); + if (container && container.length > 0) { + subViews.push(...container.reduce<ViewSpecification[]>((acc, cur) => { + if (!cur.arg) { + throw new Error(`Module: [${module.name}]. Found container without name.`); + } + const [currentView, subViews] = this.extractSubViews(cur, currentId, module, `${currentPath}/${module.name}:${cur.arg}`); + elements.push({ + id: parentId === 0 ? `${module.name}:${cur.arg}` : cur.arg, + label: cur.arg, + uiType: "object", + viewId: currentView.id, + config: config + }); + acc.push(currentView, ...subViews); + return acc; + }, [])); + } + + // process all lists + // a list is a list of containers with the leafs contained in the list + const lists = this.extractNodes(statement, "list"); + if (lists && lists.length > 0) { + subViews.push(...lists.reduce<ViewSpecification[]>((acc, cur) => { + if (!cur.arg) { + throw new Error(`Module: [${module.name}]. Found list without name.`); + } + const key = this.extractValue(cur, "key") || undefined; + if (config && !key) { + throw new Error(`Module: [${module.name}]. Found configurable list without key.`); + } + const [currentView, subViews] = this.extractSubViews(cur, currentId, module, `${currentPath}/${module.name}:${cur.arg}`); + elements.push({ + id: parentId === 0 ? `${module.name}:${cur.arg}` : cur.arg, + label: cur.arg, + isList: true, + uiType: "object", + viewId: currentView.id, + key: key, + config: config + }); + acc.push(currentView, ...subViews); + return acc; + }, [])); + } + + // process all leaf-lists + // a leaf-list is a list of some type + const leafLists = this.extractNodes(statement, "leaf-list"); + if (leafLists && leafLists.length > 0) { + elements.push(...leafLists.reduce<ViewElement[]>((acc, cur) => { + const element = this.getViewElement(cur, module, parentId, currentPath, true); + element && acc.push(element); + return acc; + }, [])); + } + + // process all leafs + // a leaf is mainly a property of an object + const leafs = this.extractNodes(statement, "leaf"); + if (leafs && leafs.length > 0) { + elements.push(...leafs.reduce<ViewElement[]>((acc, cur) => { + const element = this.getViewElement(cur, module, parentId, currentPath, false); + element && acc.push(element); + return acc; + }, [])); + } + + + const choiceStms = this.extractNodes(statement, "choice"); + if (choiceStms && choiceStms.length > 0) { + for (let i = 0; i < choiceStms.length; ++i) { + const cases = this.extractNodes(choiceStms[i], "case"); + console.warn(`Choice found ${choiceStms[i].arg}::${cases.map(c => c.arg).join(";")}`, choiceStms[i]); + } + } + + const rpcs = this.extractNodes(statement, "rpc"); + if (rpcs && rpcs.length > 0) { + // todo: + } + + if (!statement.arg) { + throw new Error(`Module: [${module.name}]. Found statement without name.`); + } + + const viewSpec: ViewSpecification = { + id: String(currentId), + parentView: String(parentId), + name: statement.arg, + title: statement.arg, + language: "en-us", + canEdit: false, + ifFeature: ifFeature, + when: whenCondition, + elements: elements.reduce<{ [name: string]: ViewElement }>((acc, cur) => { + acc[cur.id] = cur; + return acc; + }, {}), + }; + + // evaluate canEdit depending on all conditions + Object.defineProperty(viewSpec, "canEdit", { + get: () => { + return Object.keys(viewSpec.elements).some(key => { + const elm = viewSpec.elements[key]; + return (!isViewElementObjectOrList(elm) && elm.config); + }); + } + }); + + // merge in all uses references and resolve groupings + const usesRefs = this.extractNodes(statement, "uses"); + if (usesRefs && usesRefs.length > 0) { + + viewSpec.uses = (viewSpec.uses || []); + for (let i = 0; i < usesRefs.length; ++i) { + const groupingName = usesRefs[i].arg; + if (!groupingName) { + throw new Error(`Module: [${module.name}]. Found an uses statement without a grouping name.`); + } + + viewSpec.uses.push(this.resolveReferencePath(groupingName, module)); + + this._groupingsToResolve.push(() => { + const groupingViewSpec = this.resolveGrouping(groupingName, module); + if (groupingViewSpec) { + Object.keys(groupingViewSpec.elements).forEach(key => { + const elm = groupingViewSpec.elements[key]; + viewSpec.elements[key] = { + ...groupingViewSpec.elements[key], + when: elm.when ? `(${groupingViewSpec.when}) and (${elm.when})` : groupingViewSpec.when, + ifFeature: elm.ifFeature ? `(${groupingViewSpec.ifFeature}) and (${elm.ifFeature})` : groupingViewSpec.ifFeature, + }; + }); + } + }); + } + } + + return [viewSpec, subViews]; + } + + /** Extracts the UI View from the type in the cur statement. */ + private getViewElement(cur: Statement, module: Module, parentId: number, currentPath: string, isList: boolean): ViewElement { + + const type = this.extractValue(cur, "type"); + const defaultVal = this.extractValue(cur, "default") || undefined; + const description = this.extractValue(cur, "description") || undefined; + const rangeMatch = this.extractValue(cur, "range", /^(\d+)\.\.(\d+)/) || undefined; + + const configValue = this.extractValue(cur, "config"); + const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false"; + + const mandatory = this.extractValue(cur, "mandatory") === "true" || false; + + if (!cur.arg) { + throw new Error(`Module: [${module.name}]. Found element without name.`); + } + + if (!type) { + throw new Error(`Module: [${module.name}].[${cur.arg}]. Found element without type.`); + } + + const element: ViewElementBase = { + id: parentId === 0 ? `${module.name}:${cur.arg}`: cur.arg, + label: cur.arg, + config: config, + mandatory: mandatory, + isList: isList, + default: defaultVal, + description: description + }; + + if (type === "string") { + return ({ + ...element, + uiType: "string", + pattern: this.extractNodes(this.extractNodes(cur, "type")[0]!, "pattern").map(p => p.arg!).filter(p => !!p), + }); + } else if (type === "boolean") { + return ({ + ...element, + uiType: "boolean" + }); + } else if (type === "uint8") { + return ({ + ...element, + uiType: "number", + min: rangeMatch ? Number(rangeMatch[0]) : 0, + max: rangeMatch ? Number(rangeMatch[1]) : +255, + units: this.extractValue(cur, "units") || undefined, + format: this.extractValue(cur, "format") || undefined, + }); + } else if (type === "uint16") { + return ({ + ...element, + uiType: "number", + min: rangeMatch ? Number(rangeMatch[0]) : 0, + max: rangeMatch ? Number(rangeMatch[1]) : +65535, + units: this.extractValue(cur, "units") || undefined, + format: this.extractValue(cur, "format") || undefined, + }); + } else if (type === "uint32") { + return ({ + ...element, + uiType: "number", + min: rangeMatch ? Number(rangeMatch[0]) : 0, + max: rangeMatch ? Number(rangeMatch[1]) : +4294967295, + units: this.extractValue(cur, "units") || undefined, + format: this.extractValue(cur, "format") || undefined, + }); + } else if (type === "uint64") { + return ({ + ...element, + uiType: "number", + min: rangeMatch ? Number(rangeMatch[0]) : 0, + max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, + units: this.extractValue(cur, "units") || undefined, + format: this.extractValue(cur, "format") || undefined, + }); + } else if (type === "int8") { + return ({ + ...element, + uiType: "number", + min: rangeMatch ? Number(rangeMatch[0]) : -128, + max: rangeMatch ? Number(rangeMatch[1]) : +127, + units: this.extractValue(cur, "units") || undefined, + format: this.extractValue(cur, "format") || undefined, + }); + } else if (type === "int16") { + return ({ + ...element, + uiType: "number", + min: rangeMatch ? Number(rangeMatch[0]) : -32768, + max: rangeMatch ? Number(rangeMatch[1]) : +32767, + units: this.extractValue(cur, "units") || undefined, + format: this.extractValue(cur, "format") || undefined, + }); + } else if (type === "int32") { + return ({ + ...element, + uiType: "number", + min: rangeMatch ? Number(rangeMatch[0]) : -2147483648, + max: rangeMatch ? Number(rangeMatch[1]) : +2147483647, + units: this.extractValue(cur, "units") || undefined, + format: this.extractValue(cur, "format") || undefined, + }); + } else if (type === "int64") { + return ({ + ...element, + uiType: "number", + min: rangeMatch ? Number(rangeMatch[0]) : 0, + max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, + units: this.extractValue(cur, "units") || undefined, + format: this.extractValue(cur, "format") || undefined, + }); + } else if (type === "decimal16") { + return ({ + ...element, + uiType: "number", + min: rangeMatch ? Number(rangeMatch[0]) : 0, + max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, + units: this.extractValue(cur, "units") || undefined, + format: this.extractValue(cur, "format") || undefined, + fDigits: Number(this.extractValue(this.extractNodes(cur, "type")[0]!, "fraction-digits")) || -1 + }); + } else if (type === "decimal32") { + return ({ + ...element, + uiType: "number", + min: rangeMatch ? Number(rangeMatch[0]) : 0, + max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, + units: this.extractValue(cur, "units") || undefined, + format: this.extractValue(cur, "format") || undefined, + fDigits: Number(this.extractValue(this.extractNodes(cur, "type")[0]!, "fraction-digits")) || -1 + }); + } else if (type === "decimal64") { + return ({ + ...element, + uiType: "number", + min: rangeMatch ? Number(rangeMatch[0]) : 0, + max: rangeMatch ? Number(rangeMatch[1]) : +18446744073709551615, + units: this.extractValue(cur, "units") || undefined, + format: this.extractValue(cur, "format") || undefined, + fDigits: Number(this.extractValue(this.extractNodes(cur, "type")[0]!, "fraction-digits")) || -1 + }); + } else if (type === "enumeration") { + const typeNode = this.extractNodes(cur, "type")[0]!; + const enumNodes = this.extractNodes(typeNode, "enum"); + return ({ + ...element, + uiType: "selection", + options: enumNodes.reduce<{ key: string; value: string; description?: string }[]>((acc, enumNode) => { + if (!enumNode.arg) { + throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found option without name.`); + } + const ifClause = this.extractValue(enumNode, "if-feature"); + const value = this.extractValue(enumNode, "value"); + const enumOption = { + key: enumNode.arg, + value: value != null ? value : enumNode.arg, + description: this.extractValue(enumNode, "description") || undefined + }; + // todo: ❗ handle the if clause ⚡ + acc.push(enumOption); + return acc; + }, []) + }); + } else if (type === "leafref") { + const typeNode = this.extractNodes(cur, "type")[0]!; + const vPath = this.extractValue(typeNode, "path"); + if (!vPath) { + throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found leafref without path.`); + } + const refPath = this.resolveReferencePath(vPath, module); + const resolve = this.resolveReference.bind(this); + const res : ViewElement = { + ...element, + uiType: "reference", + referencePath: refPath, + ref(this: ViewElement, currentPath: string) { + const resolved = resolve(refPath, currentPath); + return resolved && { + ...resolved, + id: this.id, + label: this.label, + config: this.config, + mandatory: this.mandatory, + isList: this.isList, + default: this.default, + description: this.description, + } as ViewElement; + } + }; + return res; + } else if (type === "identityref") { + const typeNode = this.extractNodes(cur, "type")[0]!; + const base = this.extractValue(typeNode, "base"); + if (!base) { + throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found identityref without base.`); + } + const res: ViewElement = { + ...element, + uiType: "selection", + options: [] + }; + this._identityToResolve.push(() => { + const identity : Identity = this.resolveIdentity(base, module); + if (!identity) { + throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Could not resolve identity [${base}].`); + } + if (!identity.values || identity.values.length === 0) { + throw new Error(`Identity: [${base}] has no values.`); + } + res.options = identity.values.map(val => ({ + key: val.id, + value: val.id, + description: val.description + })); + }); + return res ; + } else if (type === "empty") { + // todo: ❗ handle empty ⚡ + /* 9.11. The empty Built-In Type + The empty built-in type represents a leaf that does not have any + value, it conveys information by its presence or absence. */ + console.warn(`found type: empty in [${module.name}][${currentPath}][${element.label}]`); + return { + ...element, + uiType: "string", + }; + } else if (type === "union") { + // todo: ❗ handle union ⚡ + /* 9.12. The union Built-In Type */ + console.warn(`found type: union in [${module.name}][${currentPath}][${element.label}]`); + return { + ...element, + uiType: "string", + }; + } else if (type === "bits") { + const typeNode = this.extractNodes(cur, "type")[0]!; + const bitNodes = this.extractNodes(typeNode, "bit"); + return { + ...element, + uiType: "bits", + flags: bitNodes.reduce<{[name: string]: number | undefined; }>((acc, bitNode) => { + if (!bitNode.arg) { + throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found bit without name.`); + } + const ifClause = this.extractValue(bitNode, "if-feature"); + const pos = Number(this.extractValue(bitNode, "position")); + acc[bitNode.arg] = pos === pos ? pos : undefined; + return acc; + }, {}) + }; + } else if (type === "binary") { + const typeNode = this.extractNodes(cur, "type")[0]!; + const length = Number(this.extractValue(typeNode, "length")); + return { + ...element, + uiType: "binary", + length: length === length ? length : undefined + }; + } else { + // not a build in type, have to resolve type + const typeRef = this.resolveType(type, module); + if (typeRef == null) console.error(new Error(`Could not resolve type ${type} in [${module.name}][${currentPath}].`)); + return ({ + ...typeRef, + ...element, + description: description + }) as ViewElement; + } + } + + private resolveReferencePath(vPath: string, module: Module) { + const vPathParser = /(?:(?:([^\/\:]+):)?([^\/]+))/g // 1 = opt: namespace / 2 = property + return vPath.replace(vPathParser, (_, ns, property) => { + const nameSpace = ns && module.imports[ns] || module.name; + return `${nameSpace}:${property}`; + }); + } + + + private resolveReference(vPath: string, currentPath: string) { + const vPathParser = /(?:(?:([^\/\[\]\:]+):)?([^\/\[\]]+)(\[[^\]]+\])?)/g // 1 = opt: namespace / 2 = property / 3 = opt: indexPath + let element : ViewElement | null = null; + let moduleName = ""; + + const vPathParts = splitVPath(vPath, vPathParser).map(p => ({ ns: p[1], property: p[2], ind: p[3] })); + const resultPathParts = !vPath.startsWith("/") + ? splitVPath(currentPath, vPathParser).map(p => ({ ns: p[1], property: p[2], ind: p[3] })) + : []; + + for (let i = 0; i < vPathParts.length; ++i){ + const vPathPart = vPathParts[i]; + if (vPathPart.property === "..") { + resultPathParts.pop(); + } else if (vPathPart.property !== ".") { + resultPathParts.push(vPathPart); + } + } + + // resolve element by path + for (let j = 0; j < resultPathParts.length;++j){ + const pathPart = resultPathParts[j]; + if (j===0) { + moduleName = pathPart.ns; + const rootModule = this._modules[moduleName]; + if (!rootModule) throw new Error("Could not resolve module [" + moduleName +"].\r\n" + vPath); + element = rootModule.elements[`${pathPart.ns}:${pathPart.property}`]; + } else if (element && isViewElementObjectOrList(element)) { + const view: ViewSpecification = this._views[+element.viewId]; + if (moduleName !== pathPart.ns) { + moduleName = pathPart.ns; + element = view.elements[`${moduleName}:${pathPart.property}`]; + } else { + element = view.elements[pathPart.property] || view.elements[`${moduleName}:${pathPart.property}`]; + } + } else { + throw new Error("Could not resolve reference.\r\n" + vPath); + } + if (!element) throw new Error("Could not resolve path [" + pathPart.property + "] in ["+ currentPath +"] \r\n" + vPath); + } + + return element; + } + + private resolveView(vPath: string) { + const vPathParser = /(?:(?:([^\/\[\]\:]+):)?([^\/\[\]]+)(\[[^\]]+\])?)/g // 1 = opt: namespace / 2 = property / 3 = opt: indexPath + let element: ViewElement | null = null; + let partMatch: RegExpExecArray | null; + let view: ViewSpecification | null = null; + let moduleName = ""; + if (vPath) do { + partMatch = vPathParser.exec(vPath); + if (partMatch) { + if (element === null) { + moduleName = partMatch[1]!; + const rootModule = this._modules[moduleName]; + if (!rootModule) return null; + element = rootModule.elements[`${moduleName}:${partMatch[2]!}`]; + } else if (isViewElementObjectOrList(element)) { + view = this._views[+element.viewId]; + if (moduleName !== partMatch[1]) { + moduleName = partMatch[1]; + element = view.elements[`${moduleName}:${partMatch[2]}`]; + } else { + element = view.elements[partMatch[2]]; + } + } else { + return null; + } + if (!element) return null; + } + } while (partMatch) + return element && isViewElementObjectOrList(element) && this._views[+element.viewId] || null; + } + + private resolveType(type: string, module: Module) { + const collonInd = type.indexOf(":"); + const preFix = collonInd > -1 ? type.slice(0, collonInd) : ""; + const typeName = collonInd > -1 ? type.slice(collonInd + 1) : type; + + const res = preFix + ? this._modules[module.imports[preFix]].typedefs[typeName] + : module.typedefs[typeName]; + return res; + } + + private resolveGrouping(grouping: string, module: Module) { + const collonInd = grouping.indexOf(":"); + const preFix = collonInd > -1 ? grouping.slice(0, collonInd) : ""; + const groupingName = collonInd > -1 ? grouping.slice(collonInd + 1) : grouping; + + return preFix + ? this._modules[module.imports[preFix]].groupings[groupingName] + : module.groupings[groupingName]; + + } + + private resolveIdentity(identity: string, module: Module) { + const collonInd = identity.indexOf(":"); + const preFix = collonInd > -1 ? identity.slice(0, collonInd) : ""; + const identityName = collonInd > -1 ? identity.slice(collonInd + 1) : identity; + + return preFix + ? this._modules[module.imports[preFix]].identities[identityName] + : module.identities[identityName]; + + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java b/sdnr/wt/odlux/apps/configurationApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java new file mode 100644 index 000000000..1e882fc69 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles; + +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class MyOdluxBundle extends OdluxBundle { + + @Override + public void initialize() { + super.initialize(); + } + + @Override + public void clean() { + super.clean(); + } + + @Override + public String getResourceFileContent(String filename) { + return super.getResourceFileContent(filename); + } + + @Override + public boolean hasResource(String filename) { + return super.hasResource(filename); + } + + @Override + public void setBundleName(String bundleName) { + super.setBundleName(bundleName); + } + + @Override + public void setLoader(OdluxBundleLoader loader) { + super.setLoader(loader); + } + + @Override + public String getBundleName() { + return super.getBundleName(); + } + + @Override + public OdluxBundleLoader getLoader() { + return super.getLoader(); + } + + public MyOdluxBundle() { + super(); + } +} diff --git a/sdnr/wt/odlux/apps/configurationApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml b/sdnr/wt/odlux/apps/configurationApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..48f7dde3d --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,9 @@ +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <reference id="loadersvc" availability="mandatory" activation="eager" interface="org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader"/> + + <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle"> + <property name="loader" ref="loadersvc"/> + <property name="bundleName" value="configurationApp"/> + <property name="index" value="30"/> + </bean> +</blueprint> diff --git a/sdnr/wt/odlux/apps/configurationApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java b/sdnr/wt/odlux/apps/configurationApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java new file mode 100644 index 000000000..edf68e096 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles.test; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.onap.ccsdk.features.sdnr.wt.odlux.OdluxBundleLoaderImpl; +import org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle; + +public class TestBundleRes { + + @Test + public void test() { + OdluxBundleLoaderImpl loader = OdluxBundleLoaderImpl.getInstance(); + MyOdluxBundle b = new MyOdluxBundle(); + b.setLoader(loader); + b.setIndex(0); + b.setBundleName("abc"); + b.initialize(); + assertTrue(loader.getNumberOfBundles()==1); + assertNotNull(b.getLoader()); + assertEquals("abc",b.getBundleName()); + assertTrue(b.hasResource("test.js")); + assertNotNull(b.getResourceFileContent("test.js")); + b.clean(); + assertTrue(loader.getNumberOfBundles()==0); + } + +} diff --git a/sdnr/wt/odlux/apps/configurationApp/src2/test/resources/test.js b/sdnr/wt/odlux/apps/configurationApp/src2/test/resources/test.js new file mode 100644 index 000000000..b47fdc39f --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src2/test/resources/test.js @@ -0,0 +1,5 @@ +asdac sad +as +d +sad + sadfa
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/tsconfig.json b/sdnr/wt/odlux/apps/configurationApp/tsconfig.json new file mode 100644 index 000000000..b0c9b424d --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/tsconfig.json @@ -0,0 +1,38 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": false, + "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/apps/configurationApp/webpack.config.js b/sdnr/wt/odlux/apps/configurationApp/webpack.config.js new file mode 100644 index 000000000..6a5c5d55e --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/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: { + configurationApp: ["./pluginConfiguration.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" + }] + }] + }, + + 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://10.20.6.29:48181", + secure: false + }, + "/yang-schema/": { + target: "http://10.20.6.29:48181", + secure: false + }, + "/database/": { + target: "http://10.20.6.29:48181", + secure: false + }, + "/restconf/": { + target: "http://10.20.6.29:48181", + secure: false + }, + "/help/": { + target: "http://10.20.6.29:48181", + secure: false + }, + "/websocket": { + target: "http://10.20.6.29:48181", + ws: true, + changeOrigin: false, + secure: false + } + } + } + }]; +} diff --git a/sdnr/wt/odlux/apps/connectApp/.babelrc b/sdnr/wt/odlux/apps/connectApp/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/.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/apps/connectApp/package.json b/sdnr/wt/odlux/apps/connectApp/package.json new file mode 100644 index 000000000..04653d520 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/package.json @@ -0,0 +1,40 @@ +{ + "name": "@odlux/connect-app", + "version": "0.1.1", + "description": "A react based modular UI to display network connect data from a database.", + "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": { + "@odlux/framework": "*" + }, + "peerDependencies": { + "@types/react": "16.9.11", + "@types/react-dom": "16.9.4", + "@types/react-router-dom": "4.3.1", + "@material-ui/core": "4.6.1", + "@material-ui/icons": "4.5.1", + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "jquery": "3.3.1", + "react": "16.11.0", + "react-dom": "16.11.0", + "react-router-dom": "4.3.1" + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/pom.xml b/sdnr/wt/odlux/apps/connectApp/pom.xml new file mode 100644 index 000000000..f52fa1111 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/pom.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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"> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> + <artifactId>sdnr-wt-odlux-app-connectApp</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>sdnr-wt-odlux-app-connectApp</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-provider</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src2/main/java</sourceDirectory> + <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>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src2/test/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.1</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>v10.16.3</nodeVersion> + <yarnVersion>v1.19.0</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package>org.onap.ccsdk.features.sdnr.wt.odlux.model.*,com.opensymphony.*</Import-Package> + <Private-Package/> + </instructions> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + <resource> + <directory>src2/main/resources</directory> + </resource> + <resource> + <directory>src2/test/resources</directory> + </resource> + </resources> + </build> +</project> diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts new file mode 100644 index 000000000..bf4778b5b --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts @@ -0,0 +1,113 @@ +// update action erstellen, die unterscheiden kann, ob die eine oder die andere Ansicht gerade aktive ist und diese katualisiert. +// Diese action wird dann bei jeder aktualisierung in den anderen Actions und bei eintreffen von notifikationen verwendet. + +/** + * ============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 { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import { networkElementsReloadAction } from '../handlers/networkElementsHandler'; +import { connectionStatusLogReloadAction } from '../handlers/connectionStatusLogHandler'; + +import { PanelId } from '../models/panelId'; +import { guiCutThrough } from '../models/guiCutTrough'; +import connectService from '../services/connectService'; +import { NetworkElementConnection } from '../models/networkElementConnection'; + +export class SetPanelAction extends Action { + constructor(public panelId: PanelId) { + super(); + } +} + +export class AddWebUriList extends Action { + constructor(public element: guiCutThrough[], public knownElements: string[]) { + super(); + } +} + +export class RemoveWebUri extends Action { + constructor(public element: string) { + super(); + } +} + +export const removeWebUriAction = (nodeId: string) => { + return new RemoveWebUri(nodeId); +} + +let isBusy = false; +export const findWebUrisForGuiCutThroughAsyncAction = (dispatcher: Dispatch) => (networkElements: NetworkElementConnection[], knownElements: string[]) => { + + // keep method from executing simultanously; state not used because change of iu isn't needed + if (isBusy) + return; + isBusy = true; + + const nodeIds = networkElements.map(element => { return element.id as string }); + + if (knownElements.length > 0) { + + let elementsToSearch: string[] = []; + + nodeIds.forEach(element => { + // find index of nodeId + const index = knownElements.indexOf(element); + + // if element dosen't exist, add it to list + if (index === -1) { + elementsToSearch.push(element) + } + }); + + // if new elements were found, search for weburi + if (elementsToSearch.length > 0) { + const foundWebUris = connectService.getAllWebUriExtensionsForNetworkElementListAsync(elementsToSearch); + foundWebUris.then(result => { + dispatcher(new AddWebUriList(result, elementsToSearch)); + isBusy = false; + }) + + } else { + isBusy = false; + } + + } else { + connectService.getAllWebUriExtensionsForNetworkElementListAsync(nodeIds).then(result => { + dispatcher(new AddWebUriList(result, nodeIds)); + isBusy = false; + }) + } +} + +export const setPanelAction = (panelId: PanelId) => { + return new SetPanelAction(panelId); +} + +export const updateCurrentViewAsyncAction = () => (dispatch: Dispatch, getState: () => IApplicationStoreState) => { + const { connect: { currentOpenPanel } } = getState(); + if (currentOpenPanel === "NetworkElements") { + return dispatch(networkElementsReloadAction); + } + else { + return dispatch(connectionStatusLogReloadAction); + } +}; + diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/infoNetworkElementActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/infoNetworkElementActions.ts new file mode 100644 index 000000000..4ae28aab2 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/infoNetworkElementActions.ts @@ -0,0 +1,57 @@ +/** + * ============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 { TopologyNode } from '../models/topologyNetconf'; +import { connectService } from '../services/connectService'; + +/** + * Represents the base action. + */ +export class BaseAction extends Action { } + +/** + * Represents an action causing the store to load all element Yang capabilities. + */ +export class LoadAllElementInfoAction extends BaseAction { } + +/** + * Represents an action causing the store to update element Yang capabilities. + */ +export class AllElementInfoLoadedAction extends BaseAction { + /** + * Initialize this instance. + * @param elementInfo The information of the element which is returned. + */ + constructor(public elementInfo: TopologyNode | null, public error?: string) { + super(); + } +} + +/** + * Represents an asynchronous thunk action to load all yang capabilities. + */ +export const loadAllInfoElementAsync = (nodeId: string) => (dispatch: Dispatch) => { + dispatch(new LoadAllElementInfoAction()); + connectService.infoNetworkElement(nodeId).then(info => { + dispatch(new AllElementInfoLoadedAction(info)); + }, error => { + dispatch(new AllElementInfoLoadedAction(null, error)); + }); +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/mountedNetworkElementsActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/mountedNetworkElementsActions.ts new file mode 100644 index 000000000..84e73ae5a --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/mountedNetworkElementsActions.ts @@ -0,0 +1,60 @@ +/** + * ============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 { connectService } from '../services/connectService'; +import { NetworkElementConnection } from '../models/networkElementConnection'; +import { AddSnackbarNotification } from '../../../../framework/src/actions/snackbarActions'; +import { updateCurrentViewAsyncAction } from './commonNetworkElementsActions'; + +/** Represents the base action. */ +export class BaseAction extends Action { } + +/** Represents an action crator for a async thunk action to mount a network element. */ +export const mountNetworkElementAsyncActionCreator = (networkElement: NetworkElementConnection) => (dispatch: Dispatch) => { + return connectService.mountNetworkElement(networkElement).then((success) => { + if (success) { + dispatch(updateCurrentViewAsyncAction()); + dispatch(new AddSnackbarNotification({ message: `Requesting mount [${networkElement.nodeId}]`, options: { variant: 'info' } })) + } else { + dispatch(new AddSnackbarNotification({ message: `Failed to mount [${networkElement.nodeId}]`, options: { variant: 'warning' } })); + } + }).catch(error => { + dispatch(new AddSnackbarNotification({ message: `Failed to mount [${networkElement.nodeId}]`, options: { variant: 'error' } })); + console.error(error); + }); +}; + +/** Represents an action crator for a async thunk action to unmount a network element. */ +export const unmountNetworkElementAsyncActionCreator = (nodeId: string) => (dispatch: Dispatch) => { + return connectService.unmountNetworkElement(nodeId).then((success) => { + if (success) { + dispatch(updateCurrentViewAsyncAction()); + dispatch(new AddSnackbarNotification({ message: `Requesting unmount [${nodeId}]`, options: { variant: 'info' } })); + } else { + dispatch(new AddSnackbarNotification({ message: `Failed to unmount [${nodeId}]`, options: { variant: 'warning' } })); + } + }).catch(error => { + dispatch(new AddSnackbarNotification({ message: `Failed to unmount [${nodeId}]`, options: { variant: 'error' } })); + console.error(error); + }); +}; + + diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts new file mode 100644 index 000000000..1a86f94b1 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts @@ -0,0 +1,61 @@ +/** + * ============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 { NetworkElementConnection, ConnectionStatus, UpdateNetworkElement } from '../models/networkElementConnection'; +import { connectService } from '../services/connectService'; +import { updateCurrentViewAsyncAction } from './commonNetworkElementsActions'; +import { unmountNetworkElementAsyncActionCreator } from './mountedNetworkElementsActions'; + +/** Represents the base action. */ +export class BaseAction extends Action { } + +/** Represents an async thunk action creator to add an element to the network elements. */ +export const addNewNetworkElementAsyncActionCreator = (element: NetworkElementConnection) => async (dispatch: Dispatch) => { + const res = await connectService.createNetworkElement({ ...element }); + dispatch(updateCurrentViewAsyncAction()); + dispatch(new AddSnackbarNotification({ message: `Successfully added [${element.nodeId}]`, options: { variant: 'success' } })); +}; + +/** Represents an async thunk action creator to edit network element. */ +export const editNetworkElementAsyncActionCreator = (element: UpdateNetworkElement) => async (dispatch: Dispatch) => { + const connectionStatus: ConnectionStatus[] = await connectService.getNetworkElementConnectionStatus(element.id).then(ne => (ne)) || []; + const currentConnectionStatus = connectionStatus[0].status; + if (currentConnectionStatus === "Disconnected") { + const res = await connectService.deleteNetworkElement(element); + } + else { + const res = await connectService.updateNetworkElement(element); + } + dispatch(updateCurrentViewAsyncAction()); + dispatch(new AddSnackbarNotification({ message: `Successfully modified [${element.id}]`, options: { variant: 'success' } })); +}; + + +/** Represents an async thunk action creator to delete an element from network elements. */ +export const removeNetworkElementAsyncActionCreator = (element: UpdateNetworkElement) => async (dispatch: Dispatch) => { + const res = await connectService.deleteNetworkElement(element); + await dispatch(unmountNetworkElementAsyncActionCreator(element && element.id)); + dispatch(updateCurrentViewAsyncAction()); +}; + + + diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx new file mode 100644 index 000000000..4e5ca65e1 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx @@ -0,0 +1,52 @@ +/** + * ============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 connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; + +import { createConnectionStatusLogActions, createConnectionStatusLogProperties } from '../handlers/connectionStatusLogHandler'; +import { NetworkElementConnectionLog } from '../models/networkElementConnectionLog'; + +const mapProps = (state: IApplicationStoreState) => ({ + connectionStatusLogProperties: createConnectionStatusLogProperties(state), +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + connectionStatusLogActions: createConnectionStatusLogActions(dispatcher.dispatch), +}); + +const ConnectionStatusTable = MaterialTable as MaterialTableCtorType<NetworkElementConnectionLog>; + +type ConnectionStatusLogComponentProps = Connect<typeof mapProps, typeof mapDispatch>; + +class ConnectionStatusLogComponent extends React.Component<ConnectionStatusLogComponentProps> { + render(): JSX.Element { + return ( + <ConnectionStatusTable tableId="connection-status-table" columns={[ + { property: "timestamp", title: "Time", type: ColumnType.text }, + { property: "nodeId", title: "Node Name", type: ColumnType.text }, + { property: "status", title: "Connection status", type: ColumnType.text }, + ]} idProperty="id" {...this.props.connectionStatusLogActions} {...this.props.connectionStatusLogProperties} > + </ConnectionStatusTable> + ); + }; +} + +export const ConnectionStatusLog = connect(mapProps, mapDispatch)(ConnectionStatusLogComponent); +export default ConnectionStatusLog;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx new file mode 100644 index 000000000..1e1f11523 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx @@ -0,0 +1,252 @@ +/** + * ============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 '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import { FormControl, InputLabel, Select, MenuItem } from '@material-ui/core'; + +import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; + +import { + editNetworkElementAsyncActionCreator, + addNewNetworkElementAsyncActionCreator, + removeNetworkElementAsyncActionCreator +} from '../actions/networkElementsActions'; + +import { unmountNetworkElementAsyncActionCreator, mountNetworkElementAsyncActionCreator } from '../actions/mountedNetworkElementsActions'; +import { NetworkElementConnection, UpdateNetworkElement } from '../models/networkElementConnection'; +import { removeWebUriAction } from '../actions/commonNetworkElementsActions'; + +export enum EditNetworkElementDialogMode { + None = "none", + EditNetworkElement = "editNetworkElement", + RemoveNetworkElement = "removeNetworkElement", + AddNewNetworkElement = "addNewNetworkElement", + MountNetworkElement = "mountNetworkElement", + UnmountNetworkElement = "unmountNetworkElement", +} + +const mapDispatch = (dispatcher: IDispatcher) => ({ + addNewNetworkElement: async (element: NetworkElementConnection) => { + await dispatcher.dispatch(addNewNetworkElementAsyncActionCreator(element)); + await dispatcher.dispatch(mountNetworkElementAsyncActionCreator(element)); + }, + mountNetworkElement: (element: NetworkElementConnection) => dispatcher.dispatch(mountNetworkElementAsyncActionCreator(element)), + unmountNetworkElement: (element: NetworkElementConnection) => { + dispatcher.dispatch(unmountNetworkElementAsyncActionCreator(element && element.nodeId)); + }, + editNetworkElement: async (element: UpdateNetworkElement, mountElement: NetworkElementConnection) => { + await dispatcher.dispatch(editNetworkElementAsyncActionCreator(element)); + await dispatcher.dispatch(mountNetworkElementAsyncActionCreator(mountElement)); + }, + removeNetworkElement: (element: UpdateNetworkElement) => { + dispatcher.dispatch(removeNetworkElementAsyncActionCreator(element)); + dispatcher.dispatch(removeWebUriAction(element.id)); + } +}); + +type DialogSettings = { + dialogTitle: string, + dialogDescription: string, + applyButtonText: string, + cancelButtonText: string, + enableMountIdEditor: boolean, + enableUsernameEditor: boolean, + enableExtendedEditor: boolean, +} + +const settings: { [key: string]: DialogSettings } = { + [EditNetworkElementDialogMode.None]: { + dialogTitle: "", + dialogDescription: "", + applyButtonText: "", + cancelButtonText: "", + enableMountIdEditor: false, + enableUsernameEditor: false, + enableExtendedEditor: false, + }, + + [EditNetworkElementDialogMode.AddNewNetworkElement]: { + dialogTitle: "Add new network element", + dialogDescription: "Add this new network element:", + applyButtonText: "Add network element", + cancelButtonText: "Cancel", + enableMountIdEditor: true, + enableUsernameEditor: true, + enableExtendedEditor: true, + }, + [EditNetworkElementDialogMode.MountNetworkElement]: { + dialogTitle: "Mount network element", + dialogDescription: "mount this network element:", + applyButtonText: "mount network element", + cancelButtonText: "Cancel", + enableMountIdEditor: false, + enableUsernameEditor: false, + enableExtendedEditor: false, + }, + [EditNetworkElementDialogMode.UnmountNetworkElement]: { + dialogTitle: "Unmount network element", + dialogDescription: "unmount this network element:", + applyButtonText: "Unmount network element", + cancelButtonText: "Cancel", + enableMountIdEditor: false, + enableUsernameEditor: false, + enableExtendedEditor: false, + }, + [EditNetworkElementDialogMode.EditNetworkElement]: { + dialogTitle: "Modify the network elements", + dialogDescription: "Modify this network element", + applyButtonText: "Modify", + cancelButtonText: "Cancel", + enableMountIdEditor: false, + enableUsernameEditor: true, + enableExtendedEditor: false, + }, + [EditNetworkElementDialogMode.RemoveNetworkElement]: { + dialogTitle: "Remove network element", + dialogDescription: "Do you really want to remove this network element:", + applyButtonText: "Remove network element", + cancelButtonText: "Cancel", + enableMountIdEditor: false, + enableUsernameEditor: false, + enableExtendedEditor: false, + } +} + +type EditNetworkElementDialogComponentProps = Connect<undefined, typeof mapDispatch> & { + mode: EditNetworkElementDialogMode; + initialNetworkElement: NetworkElementConnection; + onClose: () => void; +}; + +type EditNetworkElementDialogComponentState = NetworkElementConnection; + +class EditNetworkElementDialogComponent extends React.Component<EditNetworkElementDialogComponentProps, EditNetworkElementDialogComponentState> { + constructor(props: EditNetworkElementDialogComponentProps) { + super(props); + + this.state = { + nodeId: this.props.initialNetworkElement.nodeId, + isRequired: false, + host: this.props.initialNetworkElement.host, + port: this.props.initialNetworkElement.port, + }; + } + + render(): JSX.Element { + const setting = settings[this.props.mode]; + return ( + <Dialog open={this.props.mode !== EditNetworkElementDialogMode.None}> + <DialogTitle id="form-dialog-title">{setting.dialogTitle}</DialogTitle> + <DialogContent> + <DialogContentText> + {setting.dialogDescription} + </DialogContentText> + <TextField disabled={!setting.enableMountIdEditor} spellCheck={false} autoFocus margin="dense" id="name" label="Name" aria-label="name" type="text" fullWidth value={this.state.nodeId} onChange={(event) => { this.setState({ nodeId: event.target.value }); }} /> + <TextField disabled={!setting.enableMountIdEditor} spellCheck={false} margin="dense" id="ipaddress" label="IP address" aria-label="ip adress" type="text" fullWidth value={this.state.host} onChange={(event) => { this.setState({ host: event.target.value }); }} /> + <TextField disabled={!setting.enableMountIdEditor} spellCheck={false} margin="dense" id="netconfport" label="NetConf port" aria-label="netconf port" type="number" fullWidth value={this.state.port.toString()} onChange={(event) => { this.setState({ port: +event.target.value }); }} /> + {setting.enableUsernameEditor && <TextField disabled={!setting.enableUsernameEditor} spellCheck={false} margin="dense" id="username" label="Username" aria-label="username" type="text" fullWidth value={this.state.username} onChange={(event) => { this.setState({ username: event.target.value }); }} /> || null} + {setting.enableUsernameEditor && <TextField disabled={!setting.enableUsernameEditor} spellCheck={false} margin="dense" id="password" label="Password" aria-label="password" type="password" fullWidth value={this.state.password} onChange={(event) => { this.setState({ password: event.target.value }); }} /> || null} + <FormControl fullWidth disabled={!setting.enableUsernameEditor}> + <InputLabel htmlFor="active">Required</InputLabel> + <Select value={this.state.isRequired || false} onChange={(event) => { + this.setState({ isRequired: event.target.value as any as boolean }); + }} inputProps={{ name: 'required', id: 'required' }} fullWidth > + <MenuItem value={true as any as string} >True</MenuItem> + <MenuItem value={false as any as string} >False</MenuItem> + </Select> + </FormControl> + </DialogContent> + <DialogActions> + <Button onClick={(event) => { + this.onApply({ + isRequired: this.state.isRequired, + id: this.state.nodeId, + nodeId: this.state.nodeId, + host: this.state.host, + port: this.state.port, + username: this.state.username, + password: this.state.password, + }); + event.preventDefault(); + event.stopPropagation(); + }} > {setting.applyButtonText} </Button> + <Button onClick={(event) => { + this.onCancel(); + event.preventDefault(); + event.stopPropagation(); + }} color="secondary"> {setting.cancelButtonText} </Button> + </DialogActions> + </Dialog> + ) + } + + private onApply = (element: NetworkElementConnection) => { + this.props.onClose && this.props.onClose(); + let updateElement: UpdateNetworkElement = { + id: this.state.nodeId + } + switch (this.props.mode) { + case EditNetworkElementDialogMode.AddNewNetworkElement: + element && this.props.addNewNetworkElement(element); + break; + case EditNetworkElementDialogMode.MountNetworkElement: + element && this.props.mountNetworkElement(element); + break; + case EditNetworkElementDialogMode.UnmountNetworkElement: + element && this.props.unmountNetworkElement(element); + break; + case EditNetworkElementDialogMode.EditNetworkElement: + if (this.props.initialNetworkElement.isRequired !== this.state.isRequired) + updateElement.isRequired = this.state.isRequired; + if (this.props.initialNetworkElement.username !== this.state.username) + updateElement.username = this.state.username; + if (this.props.initialNetworkElement.password !== this.state.password) + updateElement.password = this.state.password; + element && this.props.editNetworkElement(updateElement, element); + break; + case EditNetworkElementDialogMode.RemoveNetworkElement: + element && this.props.removeNetworkElement(updateElement); + break; + } + }; + + private onCancel = () => { + this.props.onClose && this.props.onClose(); + } + + static getDerivedStateFromProps(props: EditNetworkElementDialogComponentProps, state: EditNetworkElementDialogComponentState & { _initialNetworkElement: NetworkElementConnection }): EditNetworkElementDialogComponentState & { _initialNetworkElement: NetworkElementConnection } { + if (props.initialNetworkElement !== state._initialNetworkElement) { + state = { + ...state, + ...props.initialNetworkElement, + _initialNetworkElement: props.initialNetworkElement, + }; + } + return state; + } +} + +export const EditNetworkElementDialog = connect(undefined, mapDispatch)(EditNetworkElementDialogComponent); +export default EditNetworkElementDialog;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/infoNetworkElementDialog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/infoNetworkElementDialog.tsx new file mode 100644 index 000000000..ea9d419ec --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/components/infoNetworkElementDialog.tsx @@ -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 * as React from 'react'; + +import Button from '@material-ui/core/Button'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; + +import { NetworkElementConnection } from '../models/networkElementConnection'; +import { AvailableCapabilities } from '../models/yangCapabilitiesType' + +export enum InfoNetworkElementDialogMode { + None = "none", + InfoNetworkElement = "infoNetworkElement" +} + +const mapDispatch = (dispatcher: IDispatcher) => ({ +}); + +type DialogSettings = { + dialogTitle: string, + dialogDescription: string, + cancelButtonText: string, +} + +const settings: { [key: string]: DialogSettings } = { + [InfoNetworkElementDialogMode.None]: { + dialogTitle: "", + dialogDescription: "", + cancelButtonText: "", + }, + [InfoNetworkElementDialogMode.InfoNetworkElement]: { + dialogTitle: "Yang capabilities of the network element", + dialogDescription: "Available capabilities of the network element", + cancelButtonText: "Cancel", + } +} + +type InfoNetworkElementDialogComponentProps = Connect<undefined, typeof mapDispatch> & { + mode: InfoNetworkElementDialogMode; + initialNetworkElement: NetworkElementConnection; + onClose: () => void; +}; + +type InfoNetworkElementDialogComponentState = NetworkElementConnection; + +class InfoNetworkElementDialogComponent extends React.Component<InfoNetworkElementDialogComponentProps, InfoNetworkElementDialogComponentState> { + constructor(props: InfoNetworkElementDialogComponentProps) { + super(props); + + this.state = { + nodeId: this.props.initialNetworkElement.nodeId, + isRequired: false, + host: this.props.initialNetworkElement.host, + port: this.props.initialNetworkElement.port, + }; + } + + render(): JSX.Element { + const setting = settings[this.props.mode]; + const availableCapabilities = this.props.state.connect.elementInfo.elementInfo["netconf-node-topology:available-capabilities"]["available-capability"]; + let yangCapabilities: AvailableCapabilities[] = []; + + availableCapabilities.forEach(value => { + const capabilty = value.capability; + const indexRevision = capabilty.indexOf("revision="); + const indexModule = capabilty.indexOf(")", indexRevision); + if (indexRevision > 0 && indexModule > 0) { + yangCapabilities.push({ + module: capabilty.substr(indexModule + 1), + revision: capabilty.substr(indexRevision + 9, 10) + }); + } + }); + + return ( + <Dialog open={this.props.mode !== InfoNetworkElementDialogMode.None}> + <DialogTitle id="form-dialog-title">{setting.dialogTitle}</DialogTitle> + <DialogContent> + <DialogContentText> + {setting.dialogDescription + " " + this.state.nodeId} + </DialogContentText> + <Table > + <TableHead> + <TableRow> + <TableCell align="right">S.No</TableCell> + <TableCell >Module</TableCell> + <TableCell >Revision</TableCell> + </TableRow> + </TableHead> + <TableBody> + {yangCapabilities.map((yang, index) => ( + <TableRow> + <TableCell>{index + 1}</TableCell> + <TableCell>{yang.module}</TableCell> + <TableCell>{yang.revision}</TableCell> + </TableRow> + ))} + </TableBody> + </Table> + </DialogContent> + <DialogActions> + <Button onClick={(event) => { + this.onCancel(); + event.preventDefault(); + event.stopPropagation(); + }} color="secondary"> {setting.cancelButtonText} </Button> + </DialogActions> + </Dialog> + ) + } + + private onCancel = () => { + this.props.onClose(); + } + + static getDerivedStateFromProps(props: InfoNetworkElementDialogComponentProps, state: InfoNetworkElementDialogComponentState & { _initialNetworkElement: NetworkElementConnection }): InfoNetworkElementDialogComponentState & { _initialNetworkElement: NetworkElementConnection } { + if (props.initialNetworkElement !== state._initialNetworkElement) { + state = { + ...state, + ...props.initialNetworkElement, + _initialNetworkElement: props.initialNetworkElement, + }; + } + return state; + } +} + +export const InfoNetworkElementDialog = connect(undefined, mapDispatch)(InfoNetworkElementDialogComponent); +export default InfoNetworkElementDialog;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx new file mode 100644 index 000000000..0f4b0e8ff --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx @@ -0,0 +1,259 @@ +/** +* ============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, createStyles, withStyles, WithStyles } from '@material-ui/core/styles'; + +import AddIcon from '@material-ui/icons/Add'; +import LinkIcon from '@material-ui/icons/Link'; +import LinkOffIcon from '@material-ui/icons/LinkOff'; +import RemoveIcon from '@material-ui/icons/RemoveCircleOutline'; +import EditIcon from '@material-ui/icons/Edit'; +import Info from '@material-ui/icons/Info'; +import ComputerIcon from '@material-ui/icons/Computer'; + +import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; +import Tooltip from '@material-ui/core/Tooltip'; + +import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions'; + +import { createNetworkElementsActions, createNetworkElementsProperties } from '../handlers/networkElementsHandler'; + +import { NetworkElementConnection } from '../models/networkElementConnection'; +import EditNetworkElementDialog, { EditNetworkElementDialogMode } from './editNetworkElementDialog'; + +import InfoNetworkElementDialog, { InfoNetworkElementDialogMode } from './infoNetworkElementDialog'; +import { loadAllInfoElementAsync } from '../actions/infoNetworkElementActions'; +import { TopologyNode } from '../models/topologyNetconf'; + +const styles = (theme: Theme) => createStyles({ + connectionStatusConnected: { + color: 'darkgreen', + }, + connectionStatusConnecting: { + color: 'blue', + }, + connectionStatusDisconnected: { + color: 'red', + }, + button: { + margin: 0, + padding: "6px 6px", + minWidth: 'unset' + }, + spacer: { + marginLeft: theme.spacing(1), + marginRight: theme.spacing(1), + display: "inline" + } +}); + +const mapProps = (state: IApplicationStoreState) => ({ + networkElementsProperties: createNetworkElementsProperties(state), +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + networkElementsActions: createNetworkElementsActions(dispatcher.dispatch), + navigateToApplication: (applicationName: string, path?: string) => dispatcher.dispatch(new NavigateToApplication(applicationName, path)), + networkElementInfo: async (nodeId: string) => await dispatcher.dispatch(loadAllInfoElementAsync(nodeId)), +}); + +type NetworkElementsListComponentProps = WithStyles<typeof styles> & Connect<typeof mapProps, typeof mapDispatch>; +type NetworkElementsListComponentState = { + networkElementToEdit: NetworkElementConnection, + networkElementEditorMode: EditNetworkElementDialogMode, + infoNetworkElementEditorMode: InfoNetworkElementDialogMode, + elementInfo: TopologyNode | null +} + +const emptyRequireNetworkElement: NetworkElementConnection = { id: "", nodeId: "", host: "", port: 0, status: "Disconnected", isRequired: false }; + +const NetworkElementTable = MaterialTable as MaterialTableCtorType<NetworkElementConnection>; + +export class NetworkElementsListComponent extends React.Component<NetworkElementsListComponentProps, NetworkElementsListComponentState> { + + constructor(props: NetworkElementsListComponentProps) { + super(props); + + this.state = { + networkElementToEdit: emptyRequireNetworkElement, + networkElementEditorMode: EditNetworkElementDialogMode.None, + elementInfo: null, + infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None + }; + } + + // private navigationCreator + + render(): JSX.Element { + const { classes } = this.props; + const { networkElementToEdit } = this.state; + const addRequireNetworkElementAction = { + icon: AddIcon, tooltip: 'Add', onClick: () => { + this.setState({ + networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement, + networkElementToEdit: emptyRequireNetworkElement, + }); + } + }; + let counter = 0; + return ( + <> + <NetworkElementTable tableId="network-element-table" customActionButtons={[addRequireNetworkElementAction]} columns={[ + { property: "nodeId", title: "Node Name", type: ColumnType.text }, + { property: "isRequired", title: "Required", type: ColumnType.boolean }, + { property: "status", title: "Connection Status", type: ColumnType.text }, + { property: "host", title: "Host", type: ColumnType.text }, + { property: "port", title: "Port", type: ColumnType.numeric }, + { property: "coreModelCapability", title: "Core Model", type: ColumnType.text }, + { property: "deviceType", title: "Type", type: ColumnType.text }, + { + property: "actions", title: "Actions", type: ColumnType.custom, customControl: ({ rowData }) => { + counter++; + return ( + <> + <div className={classes.spacer}> + { + rowData.webUri && <Tooltip title={"Web Client"} ><IconButton aria-label={"web-client-button-" + counter} className={classes.button} onClick={event => { console.log(rowData); window.open(rowData.webUri, "_blank") }}><ComputerIcon /></IconButton></Tooltip> + } + <Tooltip title={"Mount"} > + <IconButton aria-label={"mount-button-" + counter} className={classes.button} onClick={event => this.onOpenMountdNetworkElementsDialog(event, rowData)} > + <LinkIcon /></IconButton> + </Tooltip> + <Tooltip title={"Unmount"} > + <IconButton aria-label={"unmount-button-" + counter} className={classes.button} onClick={event => this.onOpenUnmountdNetworkElementsDialog(event, rowData)} > + <LinkOffIcon /></IconButton> + </Tooltip> + <Tooltip title={"Info"} ><IconButton aria-label={"info-button-" + counter} className={classes.button} onClick={event => this.onOpenInfoNetworkElementDialog(event, rowData)} disabled={rowData.status === "Connecting" || rowData.status === "Disconnected"} > + <Info /></IconButton> + </Tooltip> + <Tooltip title={"Edit"} ><IconButton aria-label={"edit-button-" + counter} className={classes.button} onClick={event => this.onOpenEditNetworkElementDialog(event, rowData)} ><EditIcon /></IconButton></Tooltip> + <Tooltip title={"Remove"} ><IconButton aria-label={"remove-button-" + counter} className={classes.button} onClick={event => this.onOpenRemoveNetworkElementDialog(event, rowData)} ><RemoveIcon /></IconButton></Tooltip> + </div> + <div className={classes.spacer}> + <Tooltip title={"Inventory"} ><Button aria-label={"inventory-button-" + counter} className={classes.button} onClick={this.navigateToApplicationHandlerCreator("inventory", rowData)} >I</Button></Tooltip> + </div> + <div className={classes.spacer}> + <Tooltip title={"Fault"} ><Button aria-label={"fault-button-" + counter} className={classes.button} onClick={this.navigateToApplicationHandlerCreator("fault", rowData)} >F</Button></Tooltip> + <Tooltip title={"Configure"} ><Button aria-label={"configure-button-" + counter} className={classes.button} onClick={this.navigateToApplicationHandlerCreator("configuration", rowData)} >C</Button></Tooltip> + <Tooltip title={"Accounting "} ><Button className={classes.button} onClick={this.navigateToApplicationHandlerCreator("accounting", rowData)} disabled={true} >A</Button></Tooltip> + <Tooltip title={"Performance"} ><Button aria-label={"performance-button-" + counter} className={classes.button} onClick={this.navigateToApplicationHandlerCreator("performanceHistory", rowData)}>P</Button></Tooltip> + <Tooltip title={"Security"} ><Button className={classes.button} onClick={this.navigateToApplicationHandlerCreator("security", rowData)} disabled={true} >S</Button></Tooltip> + </div> + </> + ) + } + }, + ]} idProperty="id" {...this.props.networkElementsActions} {...this.props.networkElementsProperties} asynchronus > + </NetworkElementTable> + <EditNetworkElementDialog + initialNetworkElement={networkElementToEdit} + mode={this.state.networkElementEditorMode} + onClose={this.onCloseEditNetworkElementDialog} + /> + <InfoNetworkElementDialog + initialNetworkElement={networkElementToEdit} + mode={this.state.infoNetworkElementEditorMode} + onClose={this.onCloseInfoNetworkElementDialog} + /> + </> + ); + }; + + public componentDidMount() { + this.props.networkElementsActions.onRefresh(); + } + + private onOpenRemoveNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => { + this.setState({ + networkElementToEdit: element, + networkElementEditorMode: EditNetworkElementDialogMode.RemoveNetworkElement + }); + event.preventDefault(); + event.stopPropagation(); + } + + private onOpenEditNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => { + this.setState({ + networkElementToEdit: { + nodeId: element.nodeId, + isRequired: element.isRequired, + host: element.host, + port: element.port, + username: element.username, + password: element.password, + }, + networkElementEditorMode: EditNetworkElementDialogMode.EditNetworkElement + }); + event.preventDefault(); + event.stopPropagation(); + } + + private onOpenUnmountdNetworkElementsDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => { + this.setState({ + networkElementToEdit: element, + networkElementEditorMode: EditNetworkElementDialogMode.UnmountNetworkElement + }); + event.preventDefault(); + event.stopPropagation(); + } + + private onOpenMountdNetworkElementsDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => { + this.setState({ + networkElementToEdit: element, + networkElementEditorMode: EditNetworkElementDialogMode.MountNetworkElement + }); + event.preventDefault(); + event.stopPropagation(); + } + + private onOpenInfoNetworkElementDialog = (event: React.MouseEvent<HTMLElement>, element: NetworkElementConnection) => { + this.props.networkElementInfo(element.nodeId); + this.setState({ + networkElementToEdit: element, + infoNetworkElementEditorMode: InfoNetworkElementDialogMode.InfoNetworkElement, + }); + event.preventDefault(); + event.stopPropagation(); + } + + private onCloseEditNetworkElementDialog = () => { + this.setState({ + networkElementEditorMode: EditNetworkElementDialogMode.None, + networkElementToEdit: emptyRequireNetworkElement, + }); + } + private onCloseInfoNetworkElementDialog = () => { + this.setState({ + infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None, + networkElementToEdit: emptyRequireNetworkElement, + }); + } + + private navigateToApplicationHandlerCreator = (applicationName: string, element: NetworkElementConnection) => (event: React.MouseEvent<HTMLElement>) => { + this.props.navigateToApplication(applicationName, element.nodeId); + event.preventDefault(); + event.stopPropagation(); + } +} + +export const NetworkElementsList = withStyles(styles)(connect(mapProps, mapDispatch)(NetworkElementsListComponent)); +export default NetworkElementsList;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts new file mode 100644 index 000000000..1440599fd --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts @@ -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========================================================================== + */ + +import { IActionHandler } from '../../../../framework/src/flux/action'; +import { combineActionHandler } from '../../../../framework/src/flux/middleware'; +import { INetworkElementsState, networkElementsActionHandler } from './networkElementsHandler'; +import { IConnectionStatusLogState, connectionStatusLogActionHandler } from './connectionStatusLogHandler'; +import { IInfoNetworkElementsState, infoNetworkElementsActionHandler } from './infoNetworkElementHandler'; +import { SetPanelAction, AddWebUriList, RemoveWebUri } from '../actions/commonNetworkElementsActions'; +import { PanelId } from '../models/panelId'; +import { guiCutThrough } from '../models/guiCutTrough'; + +export interface IConnectAppStoreState { + networkElements: INetworkElementsState; + connectionStatusLog: IConnectionStatusLogState; + currentOpenPanel: PanelId; + elementInfo: IInfoNetworkElementsState; + guiCutThrough: guiCutThroughState; +} + +const currentOpenPanelHandler: IActionHandler<PanelId> = (state = null, action) => { + if (action instanceof SetPanelAction) { + state = action.panelId; + } + return state; +} + +interface guiCutThroughState { + availableWebUris: guiCutThrough[]; + knownElements: string[]; +} + +const guiCutThroughHandler: IActionHandler<guiCutThroughState> = (state = { availableWebUris: [], knownElements: [] }, action) => { + if (action instanceof AddWebUriList) { + let knownElements: string[]; + let availableWebUris: guiCutThrough[]; + + knownElements = state.knownElements.concat(action.knownElements); + + availableWebUris = state.availableWebUris.concat(action.element); + + state = { availableWebUris: availableWebUris, knownElements: knownElements } + + } else if (action instanceof RemoveWebUri) { + const nodeId = action.element; + const webUris = state.availableWebUris.filter(item => item.nodeId !== nodeId); + const knownElements = state.knownElements.filter(item => item !== nodeId); + state = { knownElements: knownElements, availableWebUris: webUris }; + } + return state; +} + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + connect: IConnectAppStoreState + } +} + +const actionHandlers = { + networkElements: networkElementsActionHandler, + connectionStatusLog: connectionStatusLogActionHandler, + currentOpenPanel: currentOpenPanelHandler, + elementInfo: infoNetworkElementsActionHandler, + guiCutThrough: guiCutThroughHandler +}; + +export const connectAppRootHandler = combineActionHandler<IConnectAppStoreState>(actionHandlers); +export default connectAppRootHandler; diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusLogHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusLogHandler.ts new file mode 100644 index 000000000..6863ec33b --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusLogHandler.ts @@ -0,0 +1,35 @@ +/** + * ============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 { NetworkElementConnectionLog } from '../models/networkElementConnectionLog'; +export interface IConnectionStatusLogState extends IExternalTableState<NetworkElementConnectionLog> { } + +// create eleactic search material data fetch handler +const connectionStatusLogSearchHandler = createSearchDataHandler<NetworkElementConnectionLog>('connectionlog'); + +export const { + actionHandler: connectionStatusLogActionHandler, + createActions: createConnectionStatusLogActions, + createProperties: createConnectionStatusLogProperties, + reloadAction: connectionStatusLogReloadAction, + + // set value action, to change a value +} = createExternal<NetworkElementConnectionLog>(connectionStatusLogSearchHandler, appState => appState.connect.connectionStatusLog); + diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/infoNetworkElementHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/infoNetworkElementHandler.ts new file mode 100644 index 000000000..d67a81ec4 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/infoNetworkElementHandler.ts @@ -0,0 +1,60 @@ +/** + * ============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 { IActionHandler } from '../../../../framework/src/flux/action'; + +import { AllElementInfoLoadedAction, LoadAllElementInfoAction } from '../actions/infoNetworkElementActions'; + +import { TopologyNode } from '../models/topologyNetconf'; + +export interface IInfoNetworkElementsState { + elementInfo: TopologyNode; + busy: boolean; +} + +const infoNetworkElementsStateInit: IInfoNetworkElementsState = { + elementInfo: { + "node-id": "", + "netconf-node-topology:available-capabilities": { + "available-capability": [] + } + }, + busy: false +}; + +export const infoNetworkElementsActionHandler: IActionHandler<IInfoNetworkElementsState> = (state = infoNetworkElementsStateInit, action) => { + if (action instanceof LoadAllElementInfoAction) { + state = { + ...state, + busy: true + }; + } else if (action instanceof AllElementInfoLoadedAction) { + if (!action.error && action.elementInfo) { + state = { + ...state, + elementInfo: action.elementInfo, + busy: false + }; + } else { + state = { + ...state, + busy: false + }; + } + } + return state; +};
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.ts new file mode 100644 index 000000000..78c7000d2 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.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 { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; +import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch'; + +import { NetworkElementConnection } from '../models/networkElementConnection'; +import connectService from '../services/connectService'; +import { requestRest } from '../../../../framework/src/services/restService'; +export interface INetworkElementsState extends IExternalTableState<NetworkElementConnection> { } + +// create eleactic search material data fetch handler +const networkElementsSearchHandler = createSearchDataHandler<NetworkElementConnection>('network-element-connection'); + +export const { + actionHandler: networkElementsActionHandler, + createActions: createNetworkElementsActions, + createProperties: createNetworkElementsProperties, + reloadAction: networkElementsReloadAction, + + // set value action, to change a value +} = createExternal<NetworkElementConnection>(networkElementsSearchHandler, appState => { + + const webUris = appState.connect.guiCutThrough.availableWebUris; + if (appState.connect.networkElements.rows && webUris.length > 0) { + + appState.connect.networkElements.rows.forEach(element => { + + if (element.status === "Connected") { + const webUri = webUris.find(item => item.nodeId === element.id as string); + if (webUri) { + element.webUri = webUri.webUri; + element.isWebUriUnreachable = false; + } + else { + element.isWebUriUnreachable = true + } + } + }); + } + + return appState.connect.networkElements +}); + diff --git a/sdnr/wt/odlux/apps/connectApp/src/index.html b/sdnr/wt/odlux/apps/connectApp/src/index.html new file mode 100644 index 000000000..9c8a2063e --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/index.html @@ -0,0 +1,27 @@ +<!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>connectApp</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", "connectApp", "faultApp", "inventoryApp", "configurationApp"], function (app, connectApp, faultApp, inventoryApp, configurationApp) { + connectApp.register(); + faultApp.register(); + inventoryApp.register(); + app("./app.tsx").runApplication(); + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/connectionStatusLog.ts b/sdnr/wt/odlux/apps/connectApp/src/models/connectionStatusLog.ts new file mode 100644 index 000000000..df43c217a --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/models/connectionStatusLog.ts @@ -0,0 +1,27 @@ +/** + * ============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 ConnectionStatusLogType = { + _id: string; + elementStatus: string; + timeStamp: string; + objectId: string; + type: string; + newValue: string; +} + diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/guiCutTrough.ts b/sdnr/wt/odlux/apps/connectApp/src/models/guiCutTrough.ts new file mode 100644 index 000000000..4b443bac8 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/models/guiCutTrough.ts @@ -0,0 +1 @@ +export type guiCutThrough = { webUri: string, nodeId: string }
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/networkElementBase.ts b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementBase.ts new file mode 100644 index 000000000..39278a8e1 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementBase.ts @@ -0,0 +1,22 @@ +/** + * ============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 NetworkElementBaseType = { + mountId: string, + host: string, + port: number, +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts new file mode 100644 index 000000000..4c2dc9b09 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnection.ts @@ -0,0 +1,36 @@ +export type NetworkElementConnection = { + id?: string; + nodeId: string; + isRequired: boolean; + host: string; + port: number; + username?: string; + password?: string; + webUri?: string; + isWebUriUnreachable?: boolean; + status?: "Connected" | "mounted" | "unmounted" | "Connecting" | "Disconnected" | "idle"; + coreModelCapability?: string; + deviceType?: string; + nodeDetails?: { + availableCapabilites: { + capabilityOrigin: string; + capability: string; + }[]; + unavailableCapabilities: { + failureReason: string; + capability: string; + }[]; + } +} + + +export type UpdateNetworkElement = { + id: string; + isRequired?: boolean; + username?: string; + password?: string; +} + +export type ConnectionStatus = { + status: string +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnectionLog.ts b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnectionLog.ts new file mode 100644 index 000000000..a1535cbe5 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/models/networkElementConnectionLog.ts @@ -0,0 +1,25 @@ +/** + * ============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 NetworkElementConnectionLog = { + id: string; + nodeId: string; + status: "connected" | "mounted" | "unmounted" | "connecting" | "disconnected" | "idle"; + timestamp: string; +} + diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/panelId.ts b/sdnr/wt/odlux/apps/connectApp/src/models/panelId.ts new file mode 100644 index 000000000..b51412055 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/models/panelId.ts @@ -0,0 +1 @@ +export type PanelId = null | "NetworkElements" | "ConnectionStatusLog";
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/topologyNetconf.ts b/sdnr/wt/odlux/apps/connectApp/src/models/topologyNetconf.ts new file mode 100644 index 000000000..694009d1b --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/models/topologyNetconf.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========================================================================== + */ + +export interface AvailableCapability { + "capability-origin": string; + capability: string; +} + +export interface NetconfNodeTopologyAvailableCapabilities { + "available-capability": AvailableCapability[]; +} + +export interface TopologyNode { + "node-id": string; + "netconf-node-topology:available-capabilities": NetconfNodeTopologyAvailableCapabilities; +} + +export interface Topology { + "topology-id": string; + node: TopologyNode[]; +} diff --git a/sdnr/wt/odlux/apps/connectApp/src/models/yangCapabilitiesType.ts b/sdnr/wt/odlux/apps/connectApp/src/models/yangCapabilitiesType.ts new file mode 100644 index 000000000..230468287 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/models/yangCapabilitiesType.ts @@ -0,0 +1,22 @@ +/** + * ============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 AvailableCapabilities = { + module: string, + revision: string +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/pluginConnect.tsx b/sdnr/wt/odlux/apps/connectApp/src/pluginConnect.tsx new file mode 100644 index 000000000..f711c440c --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/pluginConnect.tsx @@ -0,0 +1,56 @@ +/** + * ============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 { faPlug } from '@fortawesome/free-solid-svg-icons'; + +import applicationManager from '../../../framework/src/services/applicationManager'; +import { subscribe, IFormatedMessage } from '../../../framework/src/services/notificationService'; + +import connectAppRootHandler from './handlers/connectAppRootHandler'; +import ConnectApplication from './views/connectView'; + +import { AddSnackbarNotification } from '../../../framework/src/actions/snackbarActions'; +import { updateCurrentViewAsyncAction } from './actions/commonNetworkElementsActions'; + +type ObjectNotification = { + counter: string; + nodeName: string; + objectId: string; + timeStamp: string; +} + +export function register() { + const applicationApi = applicationManager.registerApplication({ + name: "connect", + icon: faPlug, + rootComponent: ConnectApplication, + rootActionHandler: connectAppRootHandler, + menuEntry: "Connect" + }); + + // subscribe to the websocket notifications + subscribe<ObjectNotification & IFormatedMessage>(["ObjectCreationNotification", "ObjectDeletionNotification", "AttributeValueChangedNotification"], (msg => { + const store = applicationApi.applicationStore; + if (msg && msg.notifType === "ObjectCreationNotification" && store) { + store.dispatch(new AddSnackbarNotification({ message: `Adding network element [${msg.objectId}]`, options: { variant: 'info' } })); + } else if (msg && (msg.notifType === "ObjectDeletionNotification" || msg.notifType === "AttributeValueChangedNotification") && store) { + store.dispatch(new AddSnackbarNotification({ message: `Updating network element [${msg.objectId}]`, options: { variant: 'info' } })); + } + store && store.dispatch(updateCurrentViewAsyncAction()); + })); +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts b/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts new file mode 100644 index 000000000..7a410f4ae --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts @@ -0,0 +1,208 @@ +/** + * ============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 { requestRest } from '../../../../framework/src/services/restService'; +import { NetworkElementConnection, ConnectionStatus, UpdateNetworkElement } from '../models/networkElementConnection'; +import { convertPropertyNames, replaceUpperCase } from '../../../../framework/src/utilities/yangHelper'; +import { Result } from '../../../../framework/src/models/elasticSearch'; + +import { Topology, TopologyNode } from '../models/topologyNetconf'; +import { guiCutThrough } from '../models/guiCutTrough'; + +/** +* Represents a web api accessor service for all Network Elements actions. +*/ +class ConnectService { + + /** + * Inserts a network elements. + */ + public async createNetworkElement(element: NetworkElementConnection): Promise<NetworkElementConnection | null> { + const path = `/restconf/operations/data-provider:create-network-element-connection`; + const result = await requestRest<NetworkElementConnection>(path, { + method: "POST", body: JSON.stringify(convertPropertyNames({ input: element }, replaceUpperCase)) + }); + return result || null; + } + + /** + * Updates a network element. + */ + public async updateNetworkElement(element: UpdateNetworkElement): Promise<NetworkElementConnection | null> { + const path = `/restconf/operations/data-provider:update-network-element-connection`; + const result = await requestRest<NetworkElementConnection>(path, { + method: "POST", body: JSON.stringify(convertPropertyNames({ input: element }, replaceUpperCase)) + }); + return result || null; + } + + /** + * Deletes a network element. + */ + public async deleteNetworkElement(element: UpdateNetworkElement): Promise<NetworkElementConnection | null> { + const query = { + "id": element.id + }; + const path = `/restconf/operations/data-provider:delete-network-element-connection`; + const result = await requestRest<NetworkElementConnection>(path, { + method: "POST", body: JSON.stringify(convertPropertyNames({ input: query }, replaceUpperCase)) + }); + return result || null; + } + + /** Mounts network element. */ + public async mountNetworkElement(networkElement: NetworkElementConnection): Promise<boolean> { + const path = 'restconf/config/network-topology:network-topology/topology/topology-netconf/node/' + networkElement.nodeId; + const mountXml = [ + '<node xmlns="urn:TBD:params:xml:ns:yang:network-topology">', + `<node-id>${networkElement.nodeId}</node-id>`, + `<host xmlns="urn:opendaylight:netconf-node-topology">${networkElement.host}</host>`, + `<port xmlns="urn:opendaylight:netconf-node-topology">${networkElement.port}</port>`, + `<username xmlns="urn:opendaylight:netconf-node-topology">${networkElement.username}</username>`, + `<password xmlns="urn:opendaylight:netconf-node-topology">${networkElement.password}</password>`, + ' <tcp-only xmlns="urn:opendaylight:netconf-node-topology">false</tcp-only>', + + ' <!-- non-mandatory fields with default values, you can safely remove these if you do not wish to override any of these values-->', + ' <reconnect-on-changed-schema xmlns="urn:opendaylight:netconf-node-topology">false</reconnect-on-changed-schema>', + ' <connection-timeout-millis xmlns="urn:opendaylight:netconf-node-topology">20000</connection-timeout-millis>', + ' <max-connection-attempts xmlns="urn:opendaylight:netconf-node-topology">100</max-connection-attempts>', + ' <between-attempts-timeout-millis xmlns="urn:opendaylight:netconf-node-topology">2000</between-attempts-timeout-millis>', + ' <sleep-factor xmlns="urn:opendaylight:netconf-node-topology">1.5</sleep-factor>', + + ' <!-- keepalive-delay set to 0 turns off keepalives-->', + ' <keepalive-delay xmlns="urn:opendaylight:netconf-node-topology">120</keepalive-delay>', + '</node>'].join(''); + + try { + const result = await requestRest<string>(path, { + method: 'PUT', + headers: { + 'Content-Type': 'application/xml', + 'Accept': 'application/xml' + }, + body: mountXml + }); + // expect an empty answer + return result !== null; + } catch { + return false; + } + }; + + /** Unmounts a network element by its id. */ + public async unmountNetworkElement(nodeId: string): Promise<boolean> { + const path = 'restconf/config/network-topology:network-topology/topology/topology-netconf/node/' + nodeId; + + try { + const result = await requestRest<string>(path, { + method: 'DELETE', + headers: { + 'Content-Type': 'application/xml', + 'Accept': 'application/xml' + }, + }); + // expect an empty answer + return result !== null; + + } catch { + return false; + } + }; + + /** Yang capabilities of the selected network elements. */ + public async infoNetworkElement(nodeId: string): Promise<TopologyNode | null> { + const path = 'restconf/operational/network-topology:network-topology/topology/topology-netconf/node/' + nodeId; + const topologyRequestPomise = requestRest<Topology>(path, { method: "GET" }); + + return topologyRequestPomise && topologyRequestPomise.then(result => { + return result && result.node && result.node[0] || null; + }); + } + + /** + * Get the connection state of the network element. + */ + public async getNetworkElementConnectionStatus(element: string): Promise<(ConnectionStatus)[] | null> { + const path = `/restconf/operations/data-provider:read-network-element-connection-list`; + const query = { + "input": { + "filter": [{ + "property": "node-id", + "filtervalue": element + }], + "pagination": { + "size": 20, + "page": 1 + } + } + } + const result = await requestRest<Result<ConnectionStatus>>(path, { method: "POST", body: JSON.stringify(query) }); + return result && result.output && result.output.data && result.output.data.map(ne => ({ + status: ne.status + })) || null; + } + + public async getWebUriExtensionForNetworkElementAsync(ne: string) { + const path = 'restconf/config/network-topology:network-topology/topology/topology-netconf/node/' + ne + '/yang-ext:mount/core-model:network-element'; + try { + const result = await requestRest<any>(path, { method: "GET" }); + + if (result['network-element'].extension) { + const webUri = result['network-element'].extension.find((item: any) => item['value-name'] === "webUri") + if (webUri) { + return webUri.value as string; + } + } + } catch (error) { + console.log(ne + ' unrechable: ' + error) + } + + return undefined; + + } + + public getAllWebUriExtensionsForNetworkElementListAsync(ne: string[]) { + + let promises: any[] = []; + let webUris: guiCutThrough[] = [] + + ne.forEach(nodeId => { + const path = 'restconf/config/network-topology:network-topology/topology/topology-netconf/node/' + nodeId + '/yang-ext:mount/core-model:network-element'; + + // add search request to array + promises.push(requestRest<any>(path, { method: "GET" }) + .then(result => { + + if (result['network-element'] && result['network-element'].extension) { + const webUri = result['network-element'].extension.find((item: any) => item['value-name'] === "webUri") + if (webUri) { + webUris.push({ webUri: webUri.value, nodeId: nodeId }); + } + } + }) + .catch(error => console.log("network element is unreachable: " + error))) + + }) + + // wait until all promises are done and return weburis + return Promise.all(promises).then(result => { return webUris }); + } + +} +export const connectService = new ConnectService(); +export default connectService; diff --git a/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx b/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx new file mode 100644 index 000000000..aa3391c47 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx @@ -0,0 +1,111 @@ +/** + * ============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 connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { Panel } from '../../../../framework/src/components/material-ui'; +import { networkElementsReloadAction, createNetworkElementsActions } from '../handlers/networkElementsHandler'; +import { connectionStatusLogReloadAction, createConnectionStatusLogActions } from '../handlers/connectionStatusLogHandler'; + +import { NetworkElementsList } from '../components/networkElements'; +import { ConnectionStatusLog } from '../components/connectionStatusLog'; +import { setPanelAction, findWebUrisForGuiCutThroughAsyncAction } from '../actions/commonNetworkElementsActions'; +import { PanelId } from '../models/panelId'; +import { NetworkElementConnection } from 'models/networkElementConnection'; + +const mapProps = (state: IApplicationStoreState) => ({ + panelId: state.connect.currentOpenPanel, + user: state.framework.authenticationState.user, + netWorkElements: state.connect.networkElements, + availableGuiCutroughs: state.connect.guiCutThrough +}); + +const mapDispatcher = (dispatcher: IDispatcher) => ({ + networkElementsActions: createNetworkElementsActions(dispatcher.dispatch), + connectionStatusLogActions: createConnectionStatusLogActions(dispatcher.dispatch), + onLoadNetworkElements: () => { + dispatcher.dispatch(networkElementsReloadAction); + }, + loadWebUris: findWebUrisForGuiCutThroughAsyncAction(dispatcher.dispatch), + onLoadConnectionStatusLog: () => { + dispatcher.dispatch(connectionStatusLogReloadAction); + }, + switchActivePanel: (panelId: PanelId) => { + dispatcher.dispatch(setPanelAction(panelId)); + } +}); + +type ConnectApplicationComponentProps = Connect<typeof mapProps, typeof mapDispatcher>; + +class ConnectApplicationComponent extends React.Component<ConnectApplicationComponentProps>{ + + componentDidUpdate = () => { + // search for guicutthroughs after networkelements were found + const networkElements = this.props.netWorkElements; + const guiCuttrough = this.props.availableGuiCutroughs; + + if (networkElements.rows.length > 0 && networkElements.total !== guiCuttrough.knownElements.length) { + this.props.loadWebUris(networkElements.rows, guiCuttrough.knownElements); + } + } + + private onTogglePanel = (panelId: PanelId) => { + const nextActivePanel = panelId === this.props.panelId ? null : panelId; + this.props.switchActivePanel(nextActivePanel); + + switch (nextActivePanel) { + case 'NetworkElements': + this.props.onLoadNetworkElements(); + break; + case 'ConnectionStatusLog': + this.props.onLoadConnectionStatusLog(); + break; + case null: + // do nothing if all panels are closed + break; + default: + console.warn("Unknown nextActivePanel [" + nextActivePanel + "] in connectView"); + break; + } + + }; + + render(): JSX.Element { + const { panelId } = this.props; + + return ( + <> + <Panel activePanel={panelId} panelId={'NetworkElements'} onToggle={this.onTogglePanel} title={"Network Elements"}> + <NetworkElementsList /> + </Panel> + <Panel activePanel={panelId} panelId={'ConnectionStatusLog'} onToggle={this.onTogglePanel} title={"Connection Status Log"}> + <ConnectionStatusLog /> + </Panel> + </> + ); + }; + public componentDidMount() { + this.onTogglePanel("NetworkElements"); + this.props.networkElementsActions.onToggleFilter(); + this.props.connectionStatusLogActions.onToggleFilter(); + } +} + +export const ConnectApplication = (connect(mapProps, mapDispatcher)(ConnectApplicationComponent)); +export default ConnectApplication;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java b/sdnr/wt/odlux/apps/connectApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java new file mode 100644 index 000000000..1e882fc69 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles; + +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class MyOdluxBundle extends OdluxBundle { + + @Override + public void initialize() { + super.initialize(); + } + + @Override + public void clean() { + super.clean(); + } + + @Override + public String getResourceFileContent(String filename) { + return super.getResourceFileContent(filename); + } + + @Override + public boolean hasResource(String filename) { + return super.hasResource(filename); + } + + @Override + public void setBundleName(String bundleName) { + super.setBundleName(bundleName); + } + + @Override + public void setLoader(OdluxBundleLoader loader) { + super.setLoader(loader); + } + + @Override + public String getBundleName() { + return super.getBundleName(); + } + + @Override + public OdluxBundleLoader getLoader() { + return super.getLoader(); + } + + public MyOdluxBundle() { + super(); + } +} diff --git a/sdnr/wt/odlux/apps/connectApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml b/sdnr/wt/odlux/apps/connectApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..adc9b777b --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,9 @@ +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <reference id="loadersvc" availability="mandatory" activation="eager" interface="org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader"/> + + <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle"> + <property name="loader" ref="loadersvc"/> + <property name="bundleName" value="connectApp"/> + <property name="index" value="0"/> + </bean> +</blueprint>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java b/sdnr/wt/odlux/apps/connectApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java new file mode 100644 index 000000000..edf68e096 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles.test; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.onap.ccsdk.features.sdnr.wt.odlux.OdluxBundleLoaderImpl; +import org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle; + +public class TestBundleRes { + + @Test + public void test() { + OdluxBundleLoaderImpl loader = OdluxBundleLoaderImpl.getInstance(); + MyOdluxBundle b = new MyOdluxBundle(); + b.setLoader(loader); + b.setIndex(0); + b.setBundleName("abc"); + b.initialize(); + assertTrue(loader.getNumberOfBundles()==1); + assertNotNull(b.getLoader()); + assertEquals("abc",b.getBundleName()); + assertTrue(b.hasResource("test.js")); + assertNotNull(b.getResourceFileContent("test.js")); + b.clean(); + assertTrue(loader.getNumberOfBundles()==0); + } + +} diff --git a/sdnr/wt/odlux/apps/connectApp/src2/test/resources/test.js b/sdnr/wt/odlux/apps/connectApp/src2/test/resources/test.js new file mode 100644 index 000000000..b47fdc39f --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src2/test/resources/test.js @@ -0,0 +1,5 @@ +asdac sad +as +d +sad + sadfa
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/tsconfig.json b/sdnr/wt/odlux/apps/connectApp/tsconfig.json new file mode 100644 index 000000000..a66b5d828 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": false, + "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": [ + "prop-types", + "react", + "react-dom" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/sdnr/wt/odlux/apps/connectApp/webpack.config.js b/sdnr/wt/odlux/apps/connectApp/webpack.config.js new file mode 100644 index 000000000..6d70b04b0 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/webpack.config.js @@ -0,0 +1,153 @@ +/** + * 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: { + connectApp: ["./pluginConnect.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" + }] + }] + }, + + 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://10.20.6.29:28181", + secure: false + }, + "/database/": { + target: "http://10.20.6.29:28181", + secure: false + }, + "/restconf/": { + target: "http://10.20.6.29:28181", + secure: false + }, + "/help/": { + target: "http://10.20.6.29:28181", + secure: false + }, + "/websocket": { + target: "http://10.20.6.29:28181", + ws: true, + changeOrigin: true, + secure: false + } + } + } + }]; +} diff --git a/sdnr/wt/odlux/apps/demoApp/.babelrc b/sdnr/wt/odlux/apps/demoApp/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/.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/apps/demoApp/package.json b/sdnr/wt/odlux/apps/demoApp/package.json new file mode 100644 index 000000000..f692d69ed --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/package.json @@ -0,0 +1,40 @@ +{ + "name": "@odlux/demo-app", + "version": "0.1.0", + "description": "A react based modular UI framework", + "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": { + "@odlux/framework": "*" + }, + "peerDependencies": { + "@types/react": "16.9.11", + "@types/react-dom": "16.9.4", + "@types/react-router-dom": "4.3.1", + "@material-ui/core": "4.6.1", + "@material-ui/icons": "4.5.1", + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "jquery": "3.3.1", + "react": "16.11.0", + "react-dom": "16.11.0", + "react-router-dom": "4.3.1" + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/demoApp/pom.xml b/sdnr/wt/odlux/apps/demoApp/pom.xml new file mode 100644 index 000000000..8cffb9e7a --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/pom.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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"> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> + <artifactId>sdnr-wt-odlux-app-demoApp</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>sdnr-wt-odlux-app-demoApp</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-provider</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src2/main/java</sourceDirectory> + <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>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src2/test/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.1</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>v10.16.3</nodeVersion> + <yarnVersion>v1.19.0</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package>org.onap.ccsdk.features.sdnr.wt.odlux.model.*,com.opensymphony.*</Import-Package> + <Private-Package/> + </instructions> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + <resource> + <directory>src2/main/resources</directory> + </resource> + <resource> + <directory>src2/test/resources</directory> + </resource> + </resources> + </build> +</project> diff --git a/sdnr/wt/odlux/apps/demoApp/src/actions/authorActions.ts b/sdnr/wt/odlux/apps/demoApp/src/actions/authorActions.ts new file mode 100644 index 000000000..f75075192 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src/actions/authorActions.ts @@ -0,0 +1,51 @@ +/** + * ============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 { AddErrorInfoAction } from '../../../../framework/src/actions/errorActions'; + +import { IAuthor } from '../models/author'; +import { authorService } from '../services/authorService'; + +export class ApplicationBaseAction extends Action { } + + +export class LoadAllAuthorsAction extends ApplicationBaseAction { + constructor() { + super(); + } +} + +// in React Action is most times a Message +export class AllAuthorsLoadedAction extends ApplicationBaseAction { + constructor(public authors: IAuthor[] | null, public error?: string) { + super(); + + } +} + +export const loadAllAuthorsAsync = (dispatch: Dispatch) => { + dispatch(new LoadAllAuthorsAction()); + authorService.getAllAuthors().then(authors => { + dispatch(new AllAuthorsLoadedAction(authors)); + }, error => { + dispatch(new AllAuthorsLoadedAction(null, error)); + dispatch(new AddErrorInfoAction(error)); + }); +} + diff --git a/sdnr/wt/odlux/apps/demoApp/src/components/counter.tsx b/sdnr/wt/odlux/apps/demoApp/src/components/counter.tsx new file mode 100644 index 000000000..b47fa4507 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src/components/counter.tsx @@ -0,0 +1,34 @@ +/** + * ============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'; + +export class Counter extends React.Component<{}, { counter: number }> { + constructor(props: {}) { + super(props); + + this.state = { + counter: 0 + }; + } + + render() { + return ( + <button onClick={ () => this.setState({ counter: this.state.counter + 1 }) }>{ this.state.counter }</button> + ) + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/demoApp/src/handlers/demoAppRootHandler.ts b/sdnr/wt/odlux/apps/demoApp/src/handlers/demoAppRootHandler.ts new file mode 100644 index 000000000..9ff8450c8 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src/handlers/demoAppRootHandler.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========================================================================== + */ + +import { combineActionHandler } from '../../../../framework/src/flux/middleware'; + +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import { listAuthorsHandler, IListAuthors } from './listAuthorsHandler'; +import { editAuthorHandler, IEditAuthor } from './editAuthorHandler'; + +export interface IDemoAppStoreState { + listAuthors: IListAuthors; + editAuthor: IEditAuthor; +} + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + demo: IDemoAppStoreState + } +} + +const actionHandlers = { + listAuthors: listAuthorsHandler, + editAuthor: editAuthorHandler, +}; + +export const demoAppRootHandler = combineActionHandler <IDemoAppStoreState>(actionHandlers); +export default demoAppRootHandler; diff --git a/sdnr/wt/odlux/apps/demoApp/src/handlers/editAuthorHandler.ts b/sdnr/wt/odlux/apps/demoApp/src/handlers/editAuthorHandler.ts new file mode 100644 index 000000000..34b533cb1 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src/handlers/editAuthorHandler.ts @@ -0,0 +1,33 @@ +/** + * ============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 { IActionHandler } from '../../../../framework/src/flux/action'; + +import { IAuthor } from '../models/author'; +export interface IEditAuthor { + author: IAuthor | null; + isDirty: boolean; +} + +const editAuthorInit: IEditAuthor = { + author: null, + isDirty: false +}; + +export const editAuthorHandler: IActionHandler<IEditAuthor> = (state = editAuthorInit, action) => { + return state; +}; diff --git a/sdnr/wt/odlux/apps/demoApp/src/handlers/listAuthorsHandler.ts b/sdnr/wt/odlux/apps/demoApp/src/handlers/listAuthorsHandler.ts new file mode 100644 index 000000000..ca2b6d3c6 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src/handlers/listAuthorsHandler.ts @@ -0,0 +1,57 @@ +/** + * ============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 { IActionHandler } from '../../../../framework/src/flux/action'; + +import { IAuthor } from '../models/author'; +import { LoadAllAuthorsAction, AllAuthorsLoadedAction } from '../actions/authorActions'; + +export interface IListAuthors { + authors: IAuthor[]; + busy: boolean; +} + +const listAuthorsInit: IListAuthors = { + authors: [], + busy: false +}; + +export const listAuthorsHandler: IActionHandler<IListAuthors> = (state = listAuthorsInit, action) => { + if (action instanceof LoadAllAuthorsAction) { + + state = { + ...state, + busy: true + }; + + } else if (action instanceof AllAuthorsLoadedAction) { + if (!action.error && action.authors) { + state = { + ...state, + authors: action.authors, + busy: false + }; + } else { + state = { + ...state, + busy: false + }; + } + } + + return state; +};
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/demoApp/src/index.html b/sdnr/wt/odlux/apps/demoApp/src/index.html new file mode 100644 index 000000000..e85bcbb03 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src/index.html @@ -0,0 +1,25 @@ +<!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>Demo 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","demoApp"], function (app, demoApp) { + demoApp.register(); + app("./app.tsx"); + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/demoApp/src/models/author.ts b/sdnr/wt/odlux/apps/demoApp/src/models/author.ts new file mode 100644 index 000000000..0aaa308a2 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src/models/author.ts @@ -0,0 +1,37 @@ +/** + * ============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========================================================================== + */ + +/** + * Represents an author. + */ +export interface IAuthor { + /** + * Defines the unique id of the autor. + */ + id: number; + + /** + * Defines the first name of this author. + */ + firstName: string; + + /** + * Defines the last name of this author. + */ + lastName: string; +} diff --git a/sdnr/wt/odlux/apps/demoApp/src/plugin.tsx b/sdnr/wt/odlux/apps/demoApp/src/plugin.tsx new file mode 100644 index 000000000..4d67c28ac --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src/plugin.tsx @@ -0,0 +1,54 @@ +/** + * ============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 { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; + +import { faAddressBook, faRegistered } from '@fortawesome/free-solid-svg-icons'; + +import applicationManager from '../../../framework/src/services/applicationManager'; +import connect, { Connect } from '../../../framework/src/flux/connect'; + +import { demoAppRootHandler } from './handlers/demoAppRootHandler'; + +import AuthorsList from './views/authorsList'; +import EditAuthor from './views/editAuthor'; + +import { Counter } from './components/counter'; + +type AppProps = RouteComponentProps & Connect; + +const App = (props: AppProps) => ( + <Switch> + <Route exact path={ `${ props.match.path }/authors` } component={AuthorsList} /> + <Route path={ `${ props.match.path }/authors/:authorId` } component={EditAuthor } /> + <Redirect to={ `${ props.match.path }/authors` } /> + </Switch> +); + +const FinalApp = withRouter(connect()(App)); + +export function register() { + const applicationApi = applicationManager.registerApplication({ + name: "demo", + icon: faAddressBook, + rootComponent: FinalApp, + rootActionHandler: demoAppRootHandler, + exportedComponents: { counter: Counter }, + menuEntry: "Demo" + }); +} diff --git a/sdnr/wt/odlux/apps/demoApp/src/services/authorService.ts b/sdnr/wt/odlux/apps/demoApp/src/services/authorService.ts new file mode 100644 index 000000000..13e4b316c --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src/services/authorService.ts @@ -0,0 +1,72 @@ +/** + * ============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 { IAuthor } from '../models/author'; + +import * as $ from 'jquery'; + +const base_url = 'https://api.mfico.de/v1/authors'; + +/** + * Represents a web api accessor service for all author related actions. + */ +class AuthorService { + + /** + * Gets all known authors from the backend. + * @returns A promise of the type array of @see {@link IAuthor} containing all known authors. + */ + public getAllAuthors(): Promise<IAuthor[]> { + return new Promise((resolve: (value: IAuthor[]) => void, reject: (err: any) => void) => { + $.ajax({ method: "GET", url: base_url }) + .then((data) => { resolve(data); }, (err) => { reject(err) }); + }); + } + + /** + * Gets an author by its id from the backend. + * @returns A promise of the type @see {@link IAuthor} containing the author to get. + */ + public getAuthorById(id: string | number): Promise<IAuthor> { + return new Promise((resolve: (value: IAuthor) => void, reject: (err: any) => void) => { + $.ajax({ method: "GET", url: base_url + "/" + id }) + .then((data) => { resolve(data); }, (err) => { reject(err) }); + }); + } + + +/** + * Saves the given author to the backend api. + * @returns A promise of the type @see {@link IAuthor} containing the autor returned by the backend api. + */ + public saveAuthor(author: IAuthor): Promise<IAuthor> { + return new Promise((resolve: (value: IAuthor) => void, reject: (err: any) => void) => { + // simulate server save + window.setTimeout(() => { + if (Math.random() > 0.8) { + reject("Could not save author."); + } else { + resolve(author); + } + }, 800); // simulate a short network delay + }); + } +} + +// return as a singleton +export const authorService = new AuthorService(); +export default authorService; diff --git a/sdnr/wt/odlux/apps/demoApp/src/views/authorsList.tsx b/sdnr/wt/odlux/apps/demoApp/src/views/authorsList.tsx new file mode 100644 index 000000000..3d0651b49 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src/views/authorsList.tsx @@ -0,0 +1,87 @@ +/** + * ============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 { withRouter, RouteComponentProps } from 'react-router-dom'; + +import Table from '@material-ui/core/Table'; +import TableBody from '@material-ui/core/TableBody'; +import TableCell from '@material-ui/core/TableCell'; +import TableHead from '@material-ui/core/TableHead'; +import TableRow from '@material-ui/core/TableRow'; +import Paper from '@material-ui/core/Paper'; // means border + +import connect from '../../../../framework/src/flux/connect'; + +import { loadAllAuthorsAsync } from '../actions/authorActions'; +import { IAuthor } from '../models/author'; + +interface IAuthorsListProps { + authors: IAuthor[], + busy: boolean, + onLoadAllAuthors: () => void +} + +class AuthorsListComponent extends React.Component<RouteComponentProps & IAuthorsListProps> { + + render(): JSX.Element { + const { authors, busy } = this.props; + return ( + <Paper> + <Table > + <TableHead> + <TableRow> + <TableCell align="right">Id</TableCell> + <TableCell >First Name</TableCell> + <TableCell >Last Name</TableCell> + </TableRow> + </TableHead> + <TableBody> + {authors.map(author => ( + <TableRow key={author.id} onClick={(e) => this.editAuthor(author)}> + <TableCell>{author.id}</TableCell> + <TableCell>{author.firstName}</TableCell> + <TableCell>{author.lastName}</TableCell> + </TableRow> + ))} + </TableBody> + </Table> + </Paper> + ); + }; + + public componentDidMount() { + this.props.onLoadAllAuthors(); + } + + private editAuthor = (author: IAuthor) => { + author && this.props.history.push(this.props.match.path + '/' + author.id); + }; +} + +export const AuthorsList = withRouter( + connect( + ({ demo: state }) => ({ + authors: state.listAuthors.authors, + busy: state.listAuthors.busy + }), + (dispatcher) => ({ + onLoadAllAuthors: () => { + dispatcher.dispatch(loadAllAuthorsAsync) + } + }))(AuthorsListComponent)); +export default AuthorsList; diff --git a/sdnr/wt/odlux/apps/demoApp/src/views/editAuthor.tsx b/sdnr/wt/odlux/apps/demoApp/src/views/editAuthor.tsx new file mode 100644 index 000000000..92f671234 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src/views/editAuthor.tsx @@ -0,0 +1,34 @@ +/** + * ============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 { withRouter, RouteComponentProps } from 'react-router-dom'; + +type EditAuthorProps = RouteComponentProps<{ authorId: string}>; + +class EditAuthorComponent extends React.Component<EditAuthorProps> { + render(): JSX.Element { + return ( + <div> + <h2>Edit Author { this.props.match.params.authorId }</h2> + </div> + ) + } +} + +export const EditAuthor = withRouter(EditAuthorComponent); +export default EditAuthor;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/demoApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java b/sdnr/wt/odlux/apps/demoApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java new file mode 100644 index 000000000..1e882fc69 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles; + +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class MyOdluxBundle extends OdluxBundle { + + @Override + public void initialize() { + super.initialize(); + } + + @Override + public void clean() { + super.clean(); + } + + @Override + public String getResourceFileContent(String filename) { + return super.getResourceFileContent(filename); + } + + @Override + public boolean hasResource(String filename) { + return super.hasResource(filename); + } + + @Override + public void setBundleName(String bundleName) { + super.setBundleName(bundleName); + } + + @Override + public void setLoader(OdluxBundleLoader loader) { + super.setLoader(loader); + } + + @Override + public String getBundleName() { + return super.getBundleName(); + } + + @Override + public OdluxBundleLoader getLoader() { + return super.getLoader(); + } + + public MyOdluxBundle() { + super(); + } +} diff --git a/sdnr/wt/odlux/apps/demoApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml b/sdnr/wt/odlux/apps/demoApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..91f6002ac --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,9 @@ +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <reference id="loadersvc" availability="mandatory" activation="eager" interface="org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader"/> + + <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle"> + <property name="loader" ref="loadersvc"/> + <property name="bundleName" value="demoApp"/> + <property name="index" value="999"/> + </bean> +</blueprint>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/demoApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java b/sdnr/wt/odlux/apps/demoApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java new file mode 100644 index 000000000..edf68e096 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles.test; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.onap.ccsdk.features.sdnr.wt.odlux.OdluxBundleLoaderImpl; +import org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle; + +public class TestBundleRes { + + @Test + public void test() { + OdluxBundleLoaderImpl loader = OdluxBundleLoaderImpl.getInstance(); + MyOdluxBundle b = new MyOdluxBundle(); + b.setLoader(loader); + b.setIndex(0); + b.setBundleName("abc"); + b.initialize(); + assertTrue(loader.getNumberOfBundles()==1); + assertNotNull(b.getLoader()); + assertEquals("abc",b.getBundleName()); + assertTrue(b.hasResource("test.js")); + assertNotNull(b.getResourceFileContent("test.js")); + b.clean(); + assertTrue(loader.getNumberOfBundles()==0); + } + +} diff --git a/sdnr/wt/odlux/apps/demoApp/src2/test/resources/test.js b/sdnr/wt/odlux/apps/demoApp/src2/test/resources/test.js new file mode 100644 index 000000000..b47fdc39f --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/src2/test/resources/test.js @@ -0,0 +1,5 @@ +asdac sad +as +d +sad + sadfa
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/demoApp/tsconfig.json b/sdnr/wt/odlux/apps/demoApp/tsconfig.json new file mode 100644 index 000000000..a66b5d828 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": false, + "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": [ + "prop-types", + "react", + "react-dom" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/sdnr/wt/odlux/apps/demoApp/webpack.config.js b/sdnr/wt/odlux/apps/demoApp/webpack.config.js new file mode 100644 index 000000000..61dd8f6f6 --- /dev/null +++ b/sdnr/wt/odlux/apps/demoApp/webpack.config.js @@ -0,0 +1,134 @@ +/** + * 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: { + demoApp: ["./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" + }] + }] + }, + 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: { + "/api": { + target: "http://localhost:3001", + secure: false + } + } + } + }]; +} diff --git a/sdnr/wt/odlux/apps/eventLogApp/.babelrc b/sdnr/wt/odlux/apps/eventLogApp/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/.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/apps/eventLogApp/package.json b/sdnr/wt/odlux/apps/eventLogApp/package.json new file mode 100644 index 000000000..2594debc1 --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/package.json @@ -0,0 +1,40 @@ +{ + "name": "@odlux/eventlog-app", + "version": "0.1.0", + "description": "A react based modular UI to display event log from a database.", + "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": "Sai Neetha Phulmali", + "license": "Apache-2.0", + "dependencies": { + "@odlux/framework": "*" + }, + "peerDependencies": { + "@types/react": "16.9.11", + "@types/react-dom": "16.9.4", + "@types/react-router-dom": "4.3.1", + "@material-ui/core": "4.6.1", + "@material-ui/icons": "4.5.1", + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "jquery": "3.3.1", + "react": "16.11.0", + "react-dom": "16.11.0", + "react-router-dom": "4.3.1" + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/eventLogApp/pom.xml b/sdnr/wt/odlux/apps/eventLogApp/pom.xml new file mode 100644 index 000000000..9e369ee62 --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/pom.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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"> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> + <artifactId>sdnr-wt-odlux-app-eventLogApp</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>sdnr-wt-odlux-app-eventLogApp</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-provider</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src2/main/java</sourceDirectory> + <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>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src2/test/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.1</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>v10.16.3</nodeVersion> + <yarnVersion>v1.19.0</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package>org.onap.ccsdk.features.sdnr.wt.odlux.model.*,com.opensymphony.*</Import-Package> + <Private-Package/> + </instructions> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + <resource> + <directory>src2/main/resources</directory> + </resource> + <resource> + <directory>src2/test/resources</directory> + </resource> + </resources> + </build> +</project> diff --git a/sdnr/wt/odlux/apps/eventLogApp/src/handlers/eventLogAppRootHandler.ts b/sdnr/wt/odlux/apps/eventLogApp/src/handlers/eventLogAppRootHandler.ts new file mode 100644 index 000000000..6e2d40efd --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/src/handlers/eventLogAppRootHandler.ts @@ -0,0 +1,45 @@ +/** +* ============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'; + +// ** do not remove ** +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { IActionHandler } from '../../../../framework/src/flux/action'; +import { IEventLogState, eventLogActionHandler } from './eventLogHandler'; + + +export interface IEventLogAppStateState { + logEntries: IEventLogState +} + + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + eventLog: IEventLogAppStateState; + } +} + +const actionHandlers = { + logEntries: eventLogActionHandler +}; + +export const EventLogAppRootHandler = combineActionHandler<IEventLogAppStateState>(actionHandlers); +export default EventLogAppRootHandler; + diff --git a/sdnr/wt/odlux/apps/eventLogApp/src/handlers/eventLogHandler.tsx b/sdnr/wt/odlux/apps/eventLogApp/src/handlers/eventLogHandler.tsx new file mode 100644 index 000000000..300a8f7b3 --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/src/handlers/eventLogHandler.tsx @@ -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 { EventLogType } from '../models/eventLogType'; + +export interface IEventLogState extends IExternalTableState<EventLogType> { } + +// create eleactic search material data fetch handler +const eventLogSearchHandler = createSearchDataHandler<EventLogType>("eventlog"); + +export const { + actionHandler: eventLogActionHandler, + createActions: createEventLogActions, + createProperties: createEventLogProperties, + reloadAction: eventLogReloadAction, + + // set value action, to change a value +} = createExternal<EventLogType>(eventLogSearchHandler, appState => appState.eventLog.logEntries); + diff --git a/sdnr/wt/odlux/apps/eventLogApp/src/index.html b/sdnr/wt/odlux/apps/eventLogApp/src/index.html new file mode 100644 index 000000000..8027509e6 --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/src/index.html @@ -0,0 +1,26 @@ +<!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>EventLog 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","connectApp", "eventLogApp"], function (app, connectApp, eventLogApp) { + connectApp.register(); + eventLogApp.register(); + app("./app.tsx").runApplication(); + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/eventLogApp/src/models/eventLogType.ts b/sdnr/wt/odlux/apps/eventLogApp/src/models/eventLogType.ts new file mode 100644 index 000000000..7c68e6497 --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/src/models/eventLogType.ts @@ -0,0 +1,27 @@ +/** + * ============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 { HitEntry, Result } from '../../../../framework/src/models'; +export type EventLogType = { + nodeId: string; + counter: number; + timestamp: string; + objectId: string; + attributeName: string; + newValue: string; + sourceType: string; +} diff --git a/sdnr/wt/odlux/apps/eventLogApp/src/pluginEventLog.tsx b/sdnr/wt/odlux/apps/eventLogApp/src/pluginEventLog.tsx new file mode 100644 index 000000000..a2edb436f --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/src/pluginEventLog.tsx @@ -0,0 +1,40 @@ +/** +* ============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 * as React from "react"; +import { faBookOpen } from '@fortawesome/free-solid-svg-icons'; // select app icon +import applicationManager from '../../../framework/src/services/applicationManager'; + +import { EventLog } from './views/eventLog'; +import eventLogAppRootHandler from './handlers/eventLogAppRootHandler'; + +const App : React.SFC = (props) => { + return <EventLog /> +}; + +export function register() { + applicationManager.registerApplication({ + name: "eventLog", + icon: faBookOpen, + rootActionHandler: eventLogAppRootHandler, + rootComponent: App, + menuEntry: "EventLog" + }); +} + diff --git a/sdnr/wt/odlux/apps/eventLogApp/src/views/eventLog.tsx b/sdnr/wt/odlux/apps/eventLogApp/src/views/eventLog.tsx new file mode 100644 index 000000000..535de1f1d --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/src/views/eventLog.tsx @@ -0,0 +1,59 @@ +/** + * ============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 { Connect, connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; + +import { EventLogType } from '../models/eventLogType'; +import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore"; +import { createEventLogProperties, createEventLogActions } from "../handlers/eventLogHandler"; + +const EventLogTable = MaterialTable as MaterialTableCtorType<EventLogType & {_id: string}>; + +const mapProps = (state: IApplicationStoreState) => ({ + eventLogProperties: createEventLogProperties(state), + eventLog: state.eventLog.logEntries +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + eventLogActions: createEventLogActions(dispatcher.dispatch) +}); + +class EventLogComponent extends React.Component<Connect<typeof mapProps, typeof mapDispatch>> { + render() { + return <EventLogTable title="Event Log" idProperty="_id" columns={[ + { property: "nodeId", title: "Node Name" }, + { property: "counter", title: "Counter" }, + { property: "timestamp", title: "Timestamp" }, + { property: "objectId", title: "Object ID" }, + { property: "attributeName", title: "Attribute Name" }, + { property: "newValue", title: "Message" }, + { property: "sourceType", title: "Source" } + ]} {...this.props.eventLogActions} {...this.props.eventLogProperties} > + </EventLogTable> + } + + componentDidMount() { + this.props.eventLogActions.onToggleFilter(); + this.props.eventLogActions.onHandleRequestSort("node-id"); + } +} + +export const EventLog = connect(mapProps, mapDispatch)(EventLogComponent); +export default EventLog;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/eventLogApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java b/sdnr/wt/odlux/apps/eventLogApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java new file mode 100644 index 000000000..1e882fc69 --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles; + +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class MyOdluxBundle extends OdluxBundle { + + @Override + public void initialize() { + super.initialize(); + } + + @Override + public void clean() { + super.clean(); + } + + @Override + public String getResourceFileContent(String filename) { + return super.getResourceFileContent(filename); + } + + @Override + public boolean hasResource(String filename) { + return super.hasResource(filename); + } + + @Override + public void setBundleName(String bundleName) { + super.setBundleName(bundleName); + } + + @Override + public void setLoader(OdluxBundleLoader loader) { + super.setLoader(loader); + } + + @Override + public String getBundleName() { + return super.getBundleName(); + } + + @Override + public OdluxBundleLoader getLoader() { + return super.getLoader(); + } + + public MyOdluxBundle() { + super(); + } +} diff --git a/sdnr/wt/odlux/apps/eventLogApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml b/sdnr/wt/odlux/apps/eventLogApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..cb2d0626a --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,9 @@ +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <reference id="loadersvc" availability="mandatory" activation="eager" interface="org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader"/> + + <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle"> + <property name="loader" ref="loadersvc"/> + <property name="bundleName" value="eventLogApp"/> + <property name="index" value="75"/> + </bean> +</blueprint>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/eventLogApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java b/sdnr/wt/odlux/apps/eventLogApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java new file mode 100644 index 000000000..e2ffb596b --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles.test; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.onap.ccsdk.features.sdnr.wt.odlux.OdluxBundleLoaderImpl; +import org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class TestBundleRes { + + @Test + public void test() { + OdluxBundleLoader loader = OdluxBundleLoaderImpl.getInstance(); + MyOdluxBundle b = new MyOdluxBundle(); + b.setLoader(loader); + b.setIndex(0); + b.setBundleName("abc"); + b.initialize(); + assertTrue(loader.getNumberOfBundles() == 1); + assertNotNull(b.getLoader()); + assertEquals("abc",b.getBundleName()); + assertTrue(b.hasResource("test.js")); + assertNotNull(b.getResourceFileContent("test.js")); + b.clean(); + assertTrue(loader.getNumberOfBundles() == 0); + } + +} diff --git a/sdnr/wt/odlux/apps/eventLogApp/src2/test/resources/test.js b/sdnr/wt/odlux/apps/eventLogApp/src2/test/resources/test.js new file mode 100644 index 000000000..b47fdc39f --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/src2/test/resources/test.js @@ -0,0 +1,5 @@ +asdac sad +as +d +sad + sadfa
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/eventLogApp/tsconfig.json b/sdnr/wt/odlux/apps/eventLogApp/tsconfig.json new file mode 100644 index 000000000..a66b5d828 --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": false, + "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": [ + "prop-types", + "react", + "react-dom" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/sdnr/wt/odlux/apps/eventLogApp/webpack.config.js b/sdnr/wt/odlux/apps/eventLogApp/webpack.config.js new file mode 100644 index 000000000..a67a34074 --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/webpack.config.js @@ -0,0 +1,155 @@ +/** + * 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: { + eventLogApp: ["./pluginEventLog.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" + }] + }] + }, + + 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:28181", + secure: false + }, + "/database/": { + target: "http://localhost:28181", + secure: false + }, + "/restconf/": { + target: "http://localhost:28181", + secure: false + }, + "/help/": { + target: "http://localhost:28181", + secure: false + }, + "/websocket/": { + target: "http://localhost:28181", + ws: true, + changeOrigin: true, + secure: false + } + } + + } + }]; +} diff --git a/sdnr/wt/odlux/apps/faultApp/.babelrc b/sdnr/wt/odlux/apps/faultApp/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/.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/apps/faultApp/package.json b/sdnr/wt/odlux/apps/faultApp/package.json new file mode 100644 index 000000000..909626303 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/package.json @@ -0,0 +1,40 @@ +{ + "name": "@odlux/fault-app", + "version": "0.1.0", + "description": "A react based modular UI to demo the fault management.", + "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": { + "@odlux/framework": "*" + }, + "peerDependencies": { + "@types/react": "16.9.11", + "@types/react-dom": "16.9.4", + "@types/react-router-dom": "4.3.1", + "@material-ui/core": "4.6.1", + "@material-ui/icons": "4.5.1", + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "jquery": "3.3.1", + "react": "16.11.0", + "react-dom": "16.11.0", + "react-router-dom": "4.3.1" + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/pom.xml b/sdnr/wt/odlux/apps/faultApp/pom.xml new file mode 100644 index 000000000..973c1061f --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/pom.xml @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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"> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> + <artifactId>sdnr-wt-odlux-app-faultApp</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>sdnr-wt-odlux-app-faultApp</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-provider</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src2/main/java</sourceDirectory> + <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>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src2/test/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.1</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>v10.16.3</nodeVersion> + <yarnVersion>v1.19.0</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package>org.onap.ccsdk.features.sdnr.wt.odlux.model.*,com.opensymphony.*</Import-Package> + <Private-Package/> + </instructions> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + <resource> + <directory>src2/main/resources</directory> + </resource> + <resource> + <directory>src2/test/resources</directory> + </resource> + </resources> + </build> +</project> diff --git a/sdnr/wt/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.ts b/sdnr/wt/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.ts new file mode 100644 index 000000000..ba1a24822 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.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 { clearStuckAlarms } from "../services/faultStatusService" +import { Dispatch } from "../../../../framework/src/flux/store"; +import { FaultApplicationBaseAction } from "./notificationActions"; + +export class AreStuckAlarmsCleared extends FaultApplicationBaseAction { + constructor(public isBusy: boolean) { + super(); + } +} + + +export const clearStuckAlarmAsyncAction = (dispatcher: Dispatch) => async (nodeNames: string[]) => { + dispatcher(new AreStuckAlarmsCleared(true)) + const result = await clearStuckAlarms(nodeNames).catch(error => { console.error(error); return undefined }); + dispatcher(new AreStuckAlarmsCleared(false)) + return result; +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src/actions/notificationActions.ts b/sdnr/wt/odlux/apps/faultApp/src/actions/notificationActions.ts new file mode 100644 index 000000000..584e7cd8b --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/actions/notificationActions.ts @@ -0,0 +1,33 @@ +/** + * ============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 { FaultAlarmNotification } from '../models/fault'; + +export class FaultApplicationBaseAction extends Action { } + + +export class AddFaultNotificationAction extends FaultApplicationBaseAction { + constructor(public fault:FaultAlarmNotification) { + super(); + } +} + +export class ResetFaultNotificationsAction extends FaultApplicationBaseAction { + +} diff --git a/sdnr/wt/odlux/apps/faultApp/src/actions/panelChangeActions.ts b/sdnr/wt/odlux/apps/faultApp/src/actions/panelChangeActions.ts new file mode 100644 index 000000000..f032c139a --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/actions/panelChangeActions.ts @@ -0,0 +1,26 @@ +/** + * ============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 { PanelId } from '../models/panelId'; + +export class SetPanelAction extends Action { + constructor(public panelId: PanelId) { + super(); + } +} + diff --git a/sdnr/wt/odlux/apps/faultApp/src/actions/statusActions.ts b/sdnr/wt/odlux/apps/faultApp/src/actions/statusActions.ts new file mode 100644 index 000000000..8a3633243 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/actions/statusActions.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========================================================================== + */ +import { FaultApplicationBaseAction } from './notificationActions'; +import { getFaultStateFromDatabase } from '../services/faultStatusService'; +import { Dispatch } from '../../../../framework/src/flux/store'; + + +export class SetFaultStatusAction extends FaultApplicationBaseAction { + constructor (public criticalFaults: number, public majorFaults: number, public minorFaults: number, public warnings: number) { + super(); + } +} + + +export const refreshFaultStatusAsyncAction = async (dispatch: Dispatch ) => { + const result = await getFaultStateFromDatabase().catch(_=>null); + if (result) { + const statusAction = new SetFaultStatusAction( + result["Critical"] || 0, + result["Major"] || 0, + result["Minor"] || 0, + result["Warning"] || 0 + ); + dispatch(statusAction); + return; + } + dispatch(new SetFaultStatusAction(0, 0, 0, 0)); +} diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx new file mode 100644 index 000000000..3b8b9b684 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx @@ -0,0 +1,136 @@ +/** + * ============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 { DialogContent, DialogActions, Button, Dialog, DialogTitle, DialogContentText } from '@material-ui/core'; +import { currentProblemsReloadAction } from '../handlers/currentProblemsHandler'; +import { clearStuckAlarmAsyncAction } from '../actions/clearStuckAlarmsAction'; +import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect'; + +export enum ClearStuckAlarmsDialogMode { + None = "none", + Show = "show" +} + +const mapDispatch = (dispatcher: IDispatcher) => ({ + clearStuckAlarmsAsync: clearStuckAlarmAsyncAction(dispatcher.dispatch), + reloadCurrentProblemsAction: () => dispatcher.dispatch(currentProblemsReloadAction) +}) + +type clearStuckAlarmsProps = Connect<typeof undefined, typeof mapDispatch> & { + numberDevices: Number, + mode: ClearStuckAlarmsDialogMode, + stuckAlarms: string[] + onClose: () => void +} + +type ClearStuckAlarmsState = { + clearAlarmsSuccessful: boolean, + errormessage: string, + unclearedAlarms: string[] +} + +class ClearStuckAlarmsDialogComponent extends React.Component<clearStuckAlarmsProps, ClearStuckAlarmsState>{ + constructor(props: clearStuckAlarmsProps) { + super(props); + this.state = { clearAlarmsSuccessful: true, errormessage: '', unclearedAlarms: [] } + } + + onClose = (event: React.MouseEvent) => { + event.stopPropagation(); + event.preventDefault(); + this.props.onClose(); + } + + onRefresh = async (event: React.MouseEvent) => { + event.stopPropagation(); + event.preventDefault(); + const result = await this.props.clearStuckAlarmsAsync(this.props.stuckAlarms); + + if (result) { + if (result.output.nodenames) { + if (result.output.nodenames.length !== this.props.stuckAlarms.length) { //show errormessage if not all devices were cleared + const undeletedAlarm = this.props.stuckAlarms.filter(item => !result.output.nodenames.includes(item)) + const error = "The alarms of the following devices couldn't be refreshed: "; + this.setState({ clearAlarmsSuccessful: false, errormessage: error, unclearedAlarms: undeletedAlarm }) + return; + } + } + } + else { //show errormessage if no devices were cleared + this.setState({ clearAlarmsSuccessful: false, errormessage: "Alarms couldn't be refreshed.", unclearedAlarms: [] }) + return; + } + + this.props.reloadCurrentProblemsAction(); + this.props.onClose(); + } + + onOk = (event: React.MouseEvent) => { + + event.stopPropagation(); + event.preventDefault(); + if (this.state.unclearedAlarms.length > 0) + this.props.reloadCurrentProblemsAction(); + this.props.onClose(); + } + + render() { + console.log(this.props.stuckAlarms); + const device = this.props.numberDevices > 1 ? 'devices' : 'device' + const defaultMessage = "Are you sure you want to refresh all alarms for " + this.props.numberDevices + " " + device + "?" + const message = this.state.clearAlarmsSuccessful ? defaultMessage : this.state.errormessage; + + const defaultTitle = "Refresh Confirmation" + const title = this.state.clearAlarmsSuccessful ? defaultTitle : 'Refresh Result'; + + return ( + <Dialog open={this.props.mode !== ClearStuckAlarmsDialogMode.None}> + <DialogTitle>{title}</DialogTitle> + <DialogContent> + <DialogContentText> + {message} + </DialogContentText> + { + this.state.unclearedAlarms.map(item => + <DialogContentText> + {item} + </DialogContentText> + ) + } + </DialogContent> + <DialogActions> + { + this.state.clearAlarmsSuccessful && + <> + <Button onClick={this.onRefresh}>Yes</Button> + <Button onClick={this.onClose}>No</Button> + </> + } + + { + !this.state.clearAlarmsSuccessful && <Button onClick={this.onOk}>Ok</Button> + } + </DialogActions> + </Dialog> + ) + } +} + +const ClearStuckAlarmsDialog = connect(undefined, mapDispatch)(ClearStuckAlarmsDialogComponent) +export default ClearStuckAlarmsDialog; diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/faultStatus.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/faultStatus.tsx new file mode 100644 index 000000000..1ec463f47 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/components/faultStatus.tsx @@ -0,0 +1,71 @@ +/** + * ============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 { withStyles, WithStyles, createStyles, Theme } from '@material-ui/core/styles'; +import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; // select app icon + +import connect, { Connect } from '../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import Typography from '@material-ui/core/Typography'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; + +const styles = (theme: Theme) => createStyles({ + icon: { + marginLeft: 8, + marginRight: 8 + }, + critical: { + color: "red" + }, + major: { + color: "orange" + }, + minor: { + color: "#f7f700" + }, + warning: { + color: "#428bca" + } +}); + +const mapProps = (state: IApplicationStoreState) => ({ + faultStatus: state.fault.faultStatus, +}); + + +type FaultStatusComponentProps = & WithStyles<typeof styles> & Connect<typeof mapProps>; + +class FaultStatusComponent extends React.Component<FaultStatusComponentProps> { + render(): JSX.Element { + const { classes, faultStatus } = this.props; + + return ( + <Typography variant="body1" color="inherit" > + Alarm Status: <FontAwesomeIcon className={`${classes.icon} ${classes.critical}`} icon={faExclamationTriangle} /> { faultStatus.critical } | + <FontAwesomeIcon className={`${classes.icon} ${classes.major}`} icon={faExclamationTriangle} /> { faultStatus.major } | + <FontAwesomeIcon className={`${classes.icon} ${classes.minor}`} icon={faExclamationTriangle} /> { faultStatus.minor } | + <FontAwesomeIcon className={`${classes.icon} ${classes.warning}`} icon={faExclamationTriangle} /> { faultStatus.warning } | + </Typography> + ); + }; +} + +export const FaultStatus = withStyles(styles)(connect(mapProps)(FaultStatusComponent)); +export default FaultStatus;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/alarmLogEntriesHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/alarmLogEntriesHandler.ts new file mode 100644 index 000000000..31b8259b2 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/alarmLogEntriesHandler.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 { Fault } from '../models/fault'; + +export interface IAlarmLogEntriesState extends IExternalTableState<Fault> { } + +// create eleactic search data fetch handler +const alarmLogEntriesSearchHandler = createSearchDataHandler< Fault>('faultlog'); + +export const { + actionHandler: alarmLogEntriesActionHandler, + createActions: createAlarmLogEntriesActions, + createProperties: createAlarmLogEntriesProperties, + reloadAction: alarmLogEntriesReloadAction, + + // set value action, to change a value +} = createExternal<Fault>(alarmLogEntriesSearchHandler, appState => appState.fault.alarmLogEntries); + diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts new file mode 100644 index 000000000..14634b4c4 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.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 { IActionHandler } from "../../../../framework/src/flux/action" +import { AreStuckAlarmsCleared } from "../actions/clearStuckAlarmsAction"; + +export interface IStuckAlarms { + areAlarmsCleared: boolean +} + +const initialState: IStuckAlarms = { + areAlarmsCleared: false +} + +export const stuckAlarmHandler: IActionHandler<IStuckAlarms> = (state = initialState, action) => { + if (action instanceof AreStuckAlarmsCleared) { + state = { ...state, areAlarmsCleared: action.isBusy } + } + + return state; +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/currentProblemsHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/currentProblemsHandler.ts new file mode 100644 index 000000000..3698a2798 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/currentProblemsHandler.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 { Fault } from '../models/fault'; + +export interface ICurrentProblemsState extends IExternalTableState<Fault> { } + +// create eleactic search data fetch handler +const currentProblemsSearchHandler = createSearchDataHandler<Fault>('faultcurrent'); + +export const { + actionHandler: currentProblemsActionHandler, + createActions: createCurrentProblemsActions, + createProperties: createCurrentProblemsProperties, + reloadAction: currentProblemsReloadAction, + + // set value action, to change a value +} = createExternal<Fault>(currentProblemsSearchHandler, appState => appState.fault.currentProblems); + diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts new file mode 100644 index 000000000..dddb4a247 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts @@ -0,0 +1,64 @@ +/** + * ============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'; + +// ** do not remove ** +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { IActionHandler } from '../../../../framework/src/flux/action'; + +import { IFaultNotifications, faultNotificationsHandler } from './notificationsHandler'; +import { ICurrentProblemsState, currentProblemsActionHandler } from './currentProblemsHandler'; +import { IAlarmLogEntriesState, alarmLogEntriesActionHandler } from './alarmLogEntriesHandler'; +import { SetPanelAction } from '../actions/panelChangeActions'; +import { IFaultStatus, faultStatusHandler } from './faultStatusHandler'; +import { stuckAlarmHandler } from './clearStuckAlarmsHandler'; + +export interface IFaultAppStoreState { + currentProblems: ICurrentProblemsState; + faultNotifications: IFaultNotifications; + alarmLogEntries: IAlarmLogEntriesState; + currentOpenPanel: string | null; + faultStatus: IFaultStatus; +} + +const currentOpenPanelHandler: IActionHandler<string | null> = (state = null, action) => { + if (action instanceof SetPanelAction) { + state = action.panelId; + } + return state; +} + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + fault: IFaultAppStoreState; + } +} + +const actionHandlers = { + currentProblems: currentProblemsActionHandler, + faultNotifications: faultNotificationsHandler, + alarmLogEntries: alarmLogEntriesActionHandler, + currentOpenPanel: currentOpenPanelHandler, + faultStatus: faultStatusHandler, + stuckAlarms: stuckAlarmHandler +}; + +export const faultAppRootHandler = combineActionHandler<IFaultAppStoreState>(actionHandlers); +export default faultAppRootHandler; diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts new file mode 100644 index 000000000..1c76a4b1a --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts @@ -0,0 +1,46 @@ +/** + * ============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 { IActionHandler } from "../../../../framework/src/flux/action"; +import { SetFaultStatusAction } from "../actions/statusActions"; + +export interface IFaultStatus { + critical: number, + major: number, + minor: number, + warning: number +} + +const faultStatusInit: IFaultStatus = { + critical: 0, + major: 0, + minor: 0, + warning: 0 +}; + +export const faultStatusHandler: IActionHandler<IFaultStatus> = (state = faultStatusInit, action) => { + if (action instanceof SetFaultStatusAction) { + state = { + critical: action.criticalFaults, + major: action.majorFaults, + minor: action.minorFaults, + warning: action.warnings + } + } + + return state; +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/notificationsHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/notificationsHandler.ts new file mode 100644 index 000000000..aa92d2a1c --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/notificationsHandler.ts @@ -0,0 +1,47 @@ +/** + * ============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 { IActionHandler } from '../../../../framework/src/flux/action'; +import { AddFaultNotificationAction, ResetFaultNotificationsAction } from '../actions/notificationActions'; +import { FaultAlarmNotification } from '../models/fault'; + +export interface IFaultNotifications { + faults: FaultAlarmNotification[]; + since: Date; +} + +const faultNotoficationsInit: IFaultNotifications = { + faults: [], + since: new Date() +}; + +export const faultNotificationsHandler: IActionHandler<IFaultNotifications> = (state = faultNotoficationsInit, action) => { + if (action instanceof AddFaultNotificationAction) { + state = { + ...state, + faults: [...state.faults, action.fault] + }; + } else if (action instanceof ResetFaultNotificationsAction){ + state = { + ...state, + faults: [], + since: new Date() + }; + } + + return state; +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src/index.html b/sdnr/wt/odlux/apps/faultApp/src/index.html new file mode 100644 index 000000000..cd315f0b7 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/index.html @@ -0,0 +1,25 @@ +<!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>Minimal 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", "faultApp" ], function (app, faultApp) { + faultApp.register(); + app("./app.tsx").runApplication(); + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src/models/fault.ts b/sdnr/wt/odlux/apps/faultApp/src/models/fault.ts new file mode 100644 index 000000000..c65734437 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/models/fault.ts @@ -0,0 +1,67 @@ +/** + * ============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 Fault = { + id: string; + nodeId: string; + counter: number; + timestamp: string; + objectId: string; + problem: string; + severity: null | 'Warning' | 'Minor' | 'Major' | 'Critical'; + type: string; + sourceType: string; +} + +export type FaultAlarmNotification = { + id: string; + nodeName: string; + counter: number; + timeStamp: string; + objectId: string; + problem: string; + severity: null | 'Warning' | 'Minor' | 'Major' | 'Critical'; + type: string; + sourceType: string; +} + +/** + * Fault status return type + */ +export type FaultsReturnType = { + criticals: number, + majors: number, + minors: number, + warnings: number +}; + +export type FaultType = { + Critical: number, + Major: number, + Minor: number, + Warning: number +}; + +export type Faults = { + faults: FaultsReturnType +}; + +export type DeletedStuckAlarms = { + output: { + nodenames: string[] + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src/models/panelId.ts b/sdnr/wt/odlux/apps/faultApp/src/models/panelId.ts new file mode 100644 index 000000000..186aa53d9 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/models/panelId.ts @@ -0,0 +1,18 @@ +/** + * ============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 PanelId = null | "CurrentProblem" | "AlarmNotifications" | "AlarmLog";
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx b/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx new file mode 100644 index 000000000..02dde90f7 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx @@ -0,0 +1,112 @@ +/** + * ============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 * as React from "react"; +import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; + +import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect'; + +import { faBell } from '@fortawesome/free-solid-svg-icons'; // select app icon +import applicationManager from '../../../framework/src/services/applicationManager'; +import { subscribe, IFormatedMessage } from '../../../framework/src/services/notificationService'; +import { IApplicationStoreState } from "../../../framework/src/store/applicationStore"; + +import { faultAppRootHandler } from './handlers/faultAppRootHandler'; +import { FaultApplication } from "./views/faultApplication"; + +import { FaultAlarmNotification } from "./models/fault"; +import { PanelId } from "./models/panelId"; + +import { SetPanelAction } from "./actions/panelChangeActions"; +import { AddFaultNotificationAction } from "./actions/notificationActions"; + +import { createCurrentProblemsProperties, createCurrentProblemsActions, currentProblemsReloadAction } from "./handlers/currentProblemsHandler"; +import { FaultStatus } from "./components/faultStatus"; +import { refreshFaultStatusAsyncAction } from "./actions/statusActions"; + +let currentMountId: string | undefined = undefined; + +const mapProps = (state: IApplicationStoreState) => ({ + currentProblemsProperties: createCurrentProblemsProperties(state), +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + currentProblemsActions: createCurrentProblemsActions(dispatcher.dispatch, true), + setCurrentPanel: (panelId: PanelId) => dispatcher.dispatch(new SetPanelAction(panelId)) +}); + +const FaultApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ mountId?: string }> & Connect<typeof mapProps, typeof mapDisp>) => { + if (currentMountId !== props.match.params.mountId) { + // route parameter has changed + currentMountId = props.match.params.mountId || undefined; + // Hint: This timeout is need, since it is not recommended to change the state while rendering is in progress ! + window.setTimeout(() => { + if (currentMountId) { + props.setCurrentPanel("CurrentProblem"); + props.currentProblemsActions.onFilterChanged("nodeId", currentMountId); + props.currentProblemsProperties.showFilter; // || (props.currentProblemsActions.onToggleFilter()); + props.currentProblemsActions.onRefresh(); + } + }); + } + return ( + <FaultApplication /> + ) +}); + +const App = withRouter((props: RouteComponentProps) => ( + <Switch> + <Route path={ `${ props.match.path }/:mountId?` } component={ FaultApplicationRouteAdapter } /> + <Redirect to={ `${ props.match.path }` } /> + </Switch> +)); + +export function register() { + const applicationApi = applicationManager.registerApplication({ + name: "fault", + icon: faBell, + rootComponent: App, + rootActionHandler: faultAppRootHandler, + statusBarElement: FaultStatus, + menuEntry: "Fault" + }); + + // subscribe to the websocket notifications + subscribe<FaultAlarmNotification & IFormatedMessage>("ProblemNotification", (fault => { + const store = applicationApi && applicationApi.applicationStore; + if (fault && store) { + store.dispatch(new AddFaultNotificationAction(fault)); + } + })); + + applicationApi.applicationStoreInitialized.then(store => { + store.dispatch(currentProblemsReloadAction); + }); + + applicationApi.applicationStoreInitialized.then(store => { + store.dispatch(refreshFaultStatusAsyncAction); + }); + window.setInterval(() => { + applicationApi.applicationStoreInitialized.then(store => { + store.dispatch(refreshFaultStatusAsyncAction); + }); + }, 15000); + +} diff --git a/sdnr/wt/odlux/apps/faultApp/src/services/faultStatusService.ts b/sdnr/wt/odlux/apps/faultApp/src/services/faultStatusService.ts new file mode 100644 index 000000000..d3409e095 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/services/faultStatusService.ts @@ -0,0 +1,53 @@ +/** + * ============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 { requestRest } from "../../../../framework/src/services/restService"; +import { Result, PostResponse } from "../../../../framework/src/models/elasticSearch"; +import { FaultType, Faults, DeletedStuckAlarms } from "../models/fault"; + + +export const getFaultStateFromDatabase = async (): Promise<FaultType | null> => { + const path = 'restconf/operations/data-provider:read-status'; + const result = await requestRest<Result<Faults>>(path, { method: "POST" }); + + let faultType: FaultType = { + Critical: 0, + Major: 0, + Minor: 0, + Warning: 0 + } + let faults: Faults[] | null = null; + + if (result && result.output && result.output.data) { + faults = result.output.data; + faultType = { + Critical: faults[0].faults.criticals, + Major: faults[0].faults.majors, + Minor: faults[0].faults.minors, + Warning: faults[0].faults.warnings + } + } + + return faultType; +} + +export const clearStuckAlarms = async (nodeNames: string[]) => { + const path = 'restconf/operations/devicemanager:clear-current-fault-by-nodename' + const result = await requestRest<DeletedStuckAlarms>(path, { method: 'Post', body: JSON.stringify({ input: { nodenames: nodeNames } }) }) + return result; + +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx b/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx new file mode 100644 index 000000000..fd1c48ac5 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx @@ -0,0 +1,176 @@ +/** + * ============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 { withRouter, RouteComponentProps } from 'react-router-dom'; + +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; + +import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { Panel } from '../../../../framework/src/components/material-ui'; + +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + +import { Fault, FaultAlarmNotification } from '../models/fault'; +import { PanelId } from '../models/panelId'; + +import { createCurrentProblemsProperties, createCurrentProblemsActions, currentProblemsReloadAction } from '../handlers/currentProblemsHandler'; +import { createAlarmLogEntriesProperties, createAlarmLogEntriesActions, alarmLogEntriesReloadAction } from '../handlers/alarmLogEntriesHandler'; +import { SetPanelAction } from '../actions/panelChangeActions'; +import { Tooltip, IconButton } from '@material-ui/core'; +import RefreshIcon from '@material-ui/icons/Refresh'; +import ClearStuckAlarmsDialog, { ClearStuckAlarmsDialogMode } from '../components/clearStuckAlarmsDialog'; + +const mapProps = (state: IApplicationStoreState) => ({ + activePanel: state.fault.currentOpenPanel, + currentProblemsProperties: createCurrentProblemsProperties(state), + faultNotifications: state.fault.faultNotifications, + alarmLogEntriesProperties: createAlarmLogEntriesProperties(state) +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + currentProblemsActions: createCurrentProblemsActions(dispatcher.dispatch), + alarmLogEntriesActions: createAlarmLogEntriesActions(dispatcher.dispatch), + reloadCurrentProblems: () => dispatcher.dispatch(currentProblemsReloadAction), + reloadAlarmLogEntries: () => dispatcher.dispatch(alarmLogEntriesReloadAction), + setCurrentPanel: (panelId: PanelId) => dispatcher.dispatch(new SetPanelAction(panelId)), +}); + +type FaultApplicationComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp>; + +type FaultApplicationState = { + clearAlarmDialogMode: ClearStuckAlarmsDialogMode, + stuckAlarms: string[] +} + + +const FaultTable = MaterialTable as MaterialTableCtorType<Fault>; +const FaultAlarmNotificationTable = MaterialTable as MaterialTableCtorType<FaultAlarmNotification>; + + +class FaultApplicationComponent extends React.Component<FaultApplicationComponentProps, FaultApplicationState>{ + + /** + * + */ + constructor(props: FaultApplicationComponentProps) { + super(props); + this.state = { clearAlarmDialogMode: ClearStuckAlarmsDialogMode.None, stuckAlarms: [] } + } + + onDialogClose = () => { + this.setState({ clearAlarmDialogMode: ClearStuckAlarmsDialogMode.None, stuckAlarms: [] }) + } + + onDialogOpen = () => { + const stuckAlarms = [...new Set(this.props.currentProblemsProperties.rows.map(item => item.nodeId))]; + this.setState({ clearAlarmDialogMode: ClearStuckAlarmsDialogMode.Show, stuckAlarms: stuckAlarms }) + } + + + + render(): JSX.Element { + + const refreshButton = { + icon: RefreshIcon, tooltip: 'Clear stuck alarms', onClick: this.onDialogOpen + }; + const areFaultsAvailable = this.props.currentProblemsProperties.rows && this.props.currentProblemsProperties.rows.length > 0 + const customAction = areFaultsAvailable ? [refreshButton] : []; + + const { activePanel } = this.props; + + const onTogglePanel = (panelId: PanelId) => { + const nextActivePanel = panelId === this.props.activePanel ? null : panelId; + this.props.setCurrentPanel(nextActivePanel); + + switch (nextActivePanel) { + case 'CurrentProblem': + this.props.reloadCurrentProblems(); + break; + case 'AlarmLog': + this.props.reloadAlarmLogEntries(); + break; + case 'AlarmNotifications': + case null: + default: + // nothing to do + break; + } + }; + + return ( + <> + <Panel activePanel={activePanel} panelId={'CurrentProblem'} onToggle={onTogglePanel} title={'Current Problem List'}> + <FaultTable idProperty={'id'} customActionButtons={customAction} columns={[ + { property: "icon", title: "", type: ColumnType.custom, customControl: this.renderIcon }, + { property: "timestamp", type: ColumnType.text, title: "Time Stamp" }, + { property: "nodeId", title: "Node Name", type: ColumnType.text }, + { property: "counter", title: "Count", type: ColumnType.numeric, width: "100px" }, + { property: "objectId", title: "Object Id", type: ColumnType.text }, + { property: "problem", title: "Alarm Type", type: ColumnType.text }, + { property: "severity", title: "Severity", type: ColumnType.text, width: "140px" }, + ]} {...this.props.currentProblemsProperties} {...this.props.currentProblemsActions} /> + </Panel> + <Panel activePanel={activePanel} panelId={'AlarmNotifications'} onToggle={onTogglePanel} title={`Alarm Notifications ${this.props.faultNotifications.faults.length} since ${this.props.faultNotifications.since}`}> + <FaultAlarmNotificationTable rows={this.props.faultNotifications.faults} asynchronus columns={[ + { property: "icon", title: "", type: ColumnType.custom, customControl: this.renderIcon }, + { property: "timeStamp", title: "Time Stamp" }, + { property: "nodeName", title: "Node Name" }, + { property: "counter", title: "Count", width: "100px" }, + { property: "objectId", title: "Object Id" }, + { property: "problem", title: "Alarm Type" }, + { property: "severity", title: "Severity", width: "140px" }, + ]} idProperty={'id'} /> + </Panel> + <Panel activePanel={activePanel} panelId={'AlarmLog'} onToggle={onTogglePanel} title={'Alarm Log'}> + <FaultTable idProperty={'id'} columns={[ + { property: "icon", title: "", type: ColumnType.custom, customControl: this.renderIcon }, + { property: "timestamp", title: "Time Stamp" }, + { property: "nodeId", title: "Node Name" }, + { property: "counter", title: "Count", type: ColumnType.numeric, width: "100px" }, + { property: "objectId", title: "Object Id" }, + { property: "problem", title: "Alarm Type" }, + { property: "severity", title: "Severity", width: "140px" }, + { property: "sourceType", title: "Source", width: "140px" }, + ]} {...this.props.alarmLogEntriesProperties} {...this.props.alarmLogEntriesActions} /> + </Panel> + { + this.state.clearAlarmDialogMode !== ClearStuckAlarmsDialogMode.None && <ClearStuckAlarmsDialog mode={this.state.clearAlarmDialogMode} numberDevices={this.state.stuckAlarms.length} stuckAlarms={this.state.stuckAlarms} onClose={this.onDialogClose} /> + + } + + </> + ); + }; + + public componentDidMount() { + this.props.alarmLogEntriesActions.onToggleFilter(); + this.props.currentProblemsActions.onToggleFilter(); + } + private renderIcon = (props: { rowData: Fault | FaultAlarmNotification }) => { + return ( + <FontAwesomeIcon icon={faExclamationTriangle} /> + ); + }; + +} + +export const FaultApplication = withRouter(connect(mapProps, mapDisp)(FaultApplicationComponent)); +export default FaultApplication; diff --git a/sdnr/wt/odlux/apps/faultApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java b/sdnr/wt/odlux/apps/faultApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java new file mode 100644 index 000000000..1e882fc69 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles; + +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class MyOdluxBundle extends OdluxBundle { + + @Override + public void initialize() { + super.initialize(); + } + + @Override + public void clean() { + super.clean(); + } + + @Override + public String getResourceFileContent(String filename) { + return super.getResourceFileContent(filename); + } + + @Override + public boolean hasResource(String filename) { + return super.hasResource(filename); + } + + @Override + public void setBundleName(String bundleName) { + super.setBundleName(bundleName); + } + + @Override + public void setLoader(OdluxBundleLoader loader) { + super.setLoader(loader); + } + + @Override + public String getBundleName() { + return super.getBundleName(); + } + + @Override + public OdluxBundleLoader getLoader() { + return super.getLoader(); + } + + public MyOdluxBundle() { + super(); + } +} diff --git a/sdnr/wt/odlux/apps/faultApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml b/sdnr/wt/odlux/apps/faultApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..e7c262bc0 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,9 @@ +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <reference id="loadersvc" availability="mandatory" activation="eager" interface="org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader"/> + + <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle"> + <property name="loader" ref="loadersvc"/> + <property name="bundleName" value="faultApp"/> + <property name="index" value="10"/> + </bean> +</blueprint>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java b/sdnr/wt/odlux/apps/faultApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java new file mode 100644 index 000000000..edf68e096 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles.test; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.onap.ccsdk.features.sdnr.wt.odlux.OdluxBundleLoaderImpl; +import org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle; + +public class TestBundleRes { + + @Test + public void test() { + OdluxBundleLoaderImpl loader = OdluxBundleLoaderImpl.getInstance(); + MyOdluxBundle b = new MyOdluxBundle(); + b.setLoader(loader); + b.setIndex(0); + b.setBundleName("abc"); + b.initialize(); + assertTrue(loader.getNumberOfBundles()==1); + assertNotNull(b.getLoader()); + assertEquals("abc",b.getBundleName()); + assertTrue(b.hasResource("test.js")); + assertNotNull(b.getResourceFileContent("test.js")); + b.clean(); + assertTrue(loader.getNumberOfBundles()==0); + } + +} diff --git a/sdnr/wt/odlux/apps/faultApp/src2/test/resources/test.js b/sdnr/wt/odlux/apps/faultApp/src2/test/resources/test.js new file mode 100644 index 000000000..b47fdc39f --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src2/test/resources/test.js @@ -0,0 +1,5 @@ +asdac sad +as +d +sad + sadfa
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/tsconfig.json b/sdnr/wt/odlux/apps/faultApp/tsconfig.json new file mode 100644 index 000000000..a66b5d828 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": false, + "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": [ + "prop-types", + "react", + "react-dom" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/sdnr/wt/odlux/apps/faultApp/webpack.config.js b/sdnr/wt/odlux/apps/faultApp/webpack.config.js new file mode 100644 index 000000000..66926edfb --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/webpack.config.js @@ -0,0 +1,152 @@ +/** + * 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: { + faultApp: ["./pluginFault.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" + }] + }] + }, + 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://10.20.6.29:28181/", + secure: false + }, + "/database/": { + target: "http://10.20.6.29:28181/", + secure: false + }, + "/restconf/": { + target: "http://10.20.6.29:28181/", + secure: false + }, + "/help/": { + target: "http://10.20.6.29:28181/", + secure: false + }, + "/websocket/": { + target: "http://10.20.6.29:28181/", + ws: true, + changeOrigin: true, + secure: false + } + } + } + }]; +} diff --git a/sdnr/wt/odlux/apps/helpApp/.babelrc b/sdnr/wt/odlux/apps/helpApp/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/.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/apps/helpApp/package.json b/sdnr/wt/odlux/apps/helpApp/package.json new file mode 100644 index 000000000..1f106ab65 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/package.json @@ -0,0 +1,45 @@ +{ + "name": "@odlux/help-app", + "version": "0.1.0", + "description": "A react based modular UI providing the help functionaliy.", + "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": { + "@odlux/framework": "*", + "marked": "0.6.0", + "@types/marked": "0.6.0", + "highlight.js": "9.13.1", + "@types/highlight.js": "9.12.3", + "github-markdown-css": "2.10.0" + }, + "peerDependencies": { + "@types/react": "16.9.11", + "@types/react-dom": "16.9.4", + "@types/react-router-dom": "4.3.1", + "@material-ui/core": "4.6.1", + "@material-ui/icons": "4.5.1", + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "jquery": "3.3.1", + "react": "16.11.0", + "react-dom": "16.11.0", + "react-router-dom": "4.3.1" + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/helpApp/pom.xml b/sdnr/wt/odlux/apps/helpApp/pom.xml new file mode 100644 index 000000000..8180a68df --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/pom.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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"> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> + <artifactId>sdnr-wt-odlux-app-helpApp</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>sdnr-wt-odlux-app-helpApp</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-provider</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src2/main/java</sourceDirectory> + <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>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src2/test/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.1</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>v10.16.3</nodeVersion> + <yarnVersion>v1.19.0</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package>org.onap.ccsdk.features.sdnr.wt.odlux.model.*,com.opensymphony.*</Import-Package> + <Private-Package/> + </instructions> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + <resource> + <directory>src2/main/resources</directory> + </resource> + <resource> + <directory>src2/test/resources</directory> + </resource> + </resources> + </build> +</project> diff --git a/sdnr/wt/odlux/apps/helpApp/src/actions/helpActions.ts b/sdnr/wt/odlux/apps/helpApp/src/actions/helpActions.ts new file mode 100644 index 000000000..3cebfd61d --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src/actions/helpActions.ts @@ -0,0 +1,78 @@ +/** + * ============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 { TocTreeNode } from '../models/tocNode'; +import helpService from '../services/helpService'; + +export class LoadTocAction extends Action { + constructor() { + super(); + + } +} + +export class TocLoadedAction extends Action { + constructor(public toc?: TocTreeNode[], error?: string) { + super(); + + } +} + +export const requestTocAsyncAction = async (dispatch: Dispatch) => { + dispatch(new LoadTocAction); + try { + const toc = await helpService.getTableOfContents(); + if (toc) { + dispatch(new TocLoadedAction(toc)); + } else { + dispatch(new TocLoadedAction(undefined, "Could not load TOC.")); + } + } catch (err) { + dispatch(new TocLoadedAction(undefined, err)); + } +} + +export class LoadDocumentAction extends Action { + constructor() { + super(); + + } +} + +export class DocumentLoadedAction extends Action { + constructor(public document?: string, public documentPath?: string, error?: string) { + super(); + + } +} + +export const requestDocumentAsyncActionCreator = (path: string) => async (dispatch: Dispatch) => { + dispatch(new LoadDocumentAction); + try { + const doc = await helpService.getDocument(path); + if (doc) { + dispatch(new DocumentLoadedAction(doc, path)); + } else { + dispatch(new DocumentLoadedAction(undefined, undefined, "Could not load document.")); + } + } catch (err) { + dispatch(new DocumentLoadedAction(undefined, undefined, err)); + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/helpApp/src/components/helpStatus.tsx b/sdnr/wt/odlux/apps/helpApp/src/components/helpStatus.tsx new file mode 100644 index 000000000..140935715 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src/components/helpStatus.tsx @@ -0,0 +1,80 @@ +/** + * ============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 { withStyles, WithStyles, createStyles, Theme } from '@material-ui/core/styles'; +import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; // select app icon + +import connect, { Connect } from '../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import Typography from '@material-ui/core/Typography'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; +import { withRouter, RouteComponentProps } from 'react-router'; + +const styles = (theme: Theme) => createStyles({ + icon: { + marginLeft: 8, + marginRight: 8 + }, + disabled: { + color: theme.palette.grey[400] + }, + link: { + cursor: "pointer", + '&:hover': { + textDecoration: "underline" + } + } +}); + +const mapProps = (state: IApplicationStoreState) => ({ + appId: state.framework.applicationState.appId, + toc: state.help.toc +}); + + +type HelpStatusComponentProps = & RouteComponentProps & WithStyles<typeof styles> & Connect<typeof mapProps>; + +class HelpStatusComponent extends React.Component<HelpStatusComponentProps> { + render() { + const { classes, history, toc, appId } = this.props; + const rootNode = toc && toc.find(t => t.id === "sdnr"); + const helpNode = appId + ? rootNode && rootNode.nodes && rootNode.nodes.find(n => n.id === appId || n.id === appId + "App") + : rootNode; + return helpNode + ? ( + <Typography variant="body1" color="inherit" className={classes.link} onClick={(event) => { event.stopPropagation(); history.push(`/help/${helpNode.uri}`) }} > + <FontAwesomeIcon className={classes.icon} icon={faQuestionCircle} /> + Help + </Typography> + ) + : ( + <Typography variant="body1" className={classes.disabled}> + <FontAwesomeIcon className={classes.icon} icon={faQuestionCircle} /> + Help + </Typography> + ); + }; + +} + +export const HelpStatus = withRouter(withStyles(styles)(connect(mapProps)(HelpStatusComponent) as any) as any); +export default HelpStatus;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/helpApp/src/components/markdown.tsx b/sdnr/wt/odlux/apps/helpApp/src/components/markdown.tsx new file mode 100644 index 000000000..a7137836e --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src/components/markdown.tsx @@ -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========================================================================== + */ +import * as React from 'react'; + +import * as marked from 'marked'; +import * as hljs from 'highlight.js'; + +type MarkdownComponentProps = { + text: string; + className?: string; + markedOptions?: marked.MarkedOptions; + style?: React.CSSProperties +} + +const defaultRenderer = new marked.Renderer(); +defaultRenderer.link = (href, title, text) => ( + `<a target="_blank" rel="noopener noreferrer" href="${ href }" title="${ title }">${ text }</a>` +); + + +class MarkdownComponent extends React.Component<MarkdownComponentProps> { + constructor(props: MarkdownComponentProps) { + super(props); + + const markedOptions: marked.MarkedOptions = { + gfm: true, + tables: true, + breaks: false, + pedantic: false, + sanitize: true, + smartLists: true, + smartypants: false, + langPrefix: 'hljs ', + ...(this.props.markedOptions || {}), + highlight: (code, lang) => { + if (!!(lang && hljs.getLanguage(lang))) { + return hljs.highlight(lang, code).value; + } + return code; + } + }; + + marked.setOptions(markedOptions); + } + render() { + const { text, className, style } = this.props; + + + const html = (marked(text || '', { renderer: this.props.markedOptions && this.props.markedOptions.renderer || defaultRenderer })); + + return ( + <div + dangerouslySetInnerHTML={ { __html: html } } + className={ className } + style={ style } + /> + ); + } +} + +export const Markdown = MarkdownComponent; + diff --git a/sdnr/wt/odlux/apps/helpApp/src/components/tocEntry.tsx b/sdnr/wt/odlux/apps/helpApp/src/components/tocEntry.tsx new file mode 100644 index 000000000..fc7b8dff5 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src/components/tocEntry.tsx @@ -0,0 +1,82 @@ +/** + * ============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 { TocTreeNode } from "../models/tocNode" +import { Typography, Link, makeStyles, Theme, createStyles } from "@material-ui/core" + +const useStyles = makeStyles((theme: Theme) => + createStyles({ + link: { + color: "blue", + }, + sublink: { + margin: theme.spacing(1), + color: "blue", + }, + container: { + display: "flex", + flexDirection: "row", + flexWrap: "wrap", + } + }), +); + +type tocEntryProps = { + label: string, + overviewUri: string, + nodes?: TocTreeNode[], + loadDocument(uri: string): any +} + +const TocEntry: React.FunctionComponent<tocEntryProps> = (props) => { + const classes = useStyles(); + const areNodesEmpty = !props.nodes || props.nodes.length === 0 + + const navigate = (event: React.SyntheticEvent, uri: string) => { + event.preventDefault(); + event.stopPropagation(); + props.loadDocument(uri); + } + + return (<div> + { + areNodesEmpty ? <Typography variant="h6"> + <Link onClick={(event: any) => navigate(event, props.overviewUri)} className={classes.link}> {props.label}</Link> + </Typography> : + <> + <Typography variant="h6"> + {props.label} + </Typography> + <div className={classes.container}> + <Typography variant="body1"> + <Link onClick={(event: any) => navigate(event, props.overviewUri)} className={classes.sublink}>Overview</Link> + </Typography> + {props.nodes !== undefined && props.nodes.map((item, index) => + <Typography variant="body1" key={index + 'x' + item.id}> + <Link onClick={(event: any) => navigate(event, item.uri)} className={classes.sublink}>{item.label}</Link> + </Typography> + )} + </div> + </> + } + </div >) +} + + +export default TocEntry;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/helpApp/src/handlers/helpAppRootHandler.ts b/sdnr/wt/odlux/apps/helpApp/src/handlers/helpAppRootHandler.ts new file mode 100644 index 000000000..cc6a98488 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src/handlers/helpAppRootHandler.ts @@ -0,0 +1,75 @@ +/** + * ============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 { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { TocTreeNode } from 'models/tocNode'; +import { IActionHandler } from '../../../../framework/src/flux/action'; +import { LoadTocAction, TocLoadedAction, LoadDocumentAction, DocumentLoadedAction } from '../actions/helpActions'; + +export interface IHelpAppStoreState { + busy: boolean; + toc: TocTreeNode[] | undefined; + content: string | undefined; + currentPath: string | undefined; +} + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + help: IHelpAppStoreState + } +} + +const helpAppStoreStatcurrentPatheInit: IHelpAppStoreState = { + busy: false, + toc: undefined, + content: undefined, + currentPath: undefined +}; + +export const helpAppRootHandler: IActionHandler<IHelpAppStoreState> = (state = helpAppStoreStatcurrentPatheInit, action) => { + if (action instanceof LoadTocAction) { + state = { + ...state, + busy: true + }; + } else if (action instanceof TocLoadedAction) { + state = { + ...state, + busy: false, + toc: action.toc + }; + } else if (action instanceof LoadDocumentAction) { + state = { + ...state, + busy: true + }; + } else if (action instanceof DocumentLoadedAction) { + state = { + ...state, + busy: false, + content: action.document, + currentPath: action.documentPath + }; + } + + return state; +} + + +export default helpAppRootHandler; diff --git a/sdnr/wt/odlux/apps/helpApp/src/index.html b/sdnr/wt/odlux/apps/helpApp/src/index.html new file mode 100644 index 000000000..2344708c8 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/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>Minimal 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", "helpApp"], function (app, helpApp) { + //connectApp.register(); + //faultApp.register(); + //configurationApp.register(); + //maintenanceApp.register(); + helpApp.register(); + app("./app.tsx").runApplication(); + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/helpApp/src/models/tocNode.ts b/sdnr/wt/odlux/apps/helpApp/src/models/tocNode.ts new file mode 100644 index 000000000..dbefeec77 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src/models/tocNode.ts @@ -0,0 +1,42 @@ +/** + * ============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 VersionInfo = { + label: string, + path: string, + date: string +} + +export type TocNode = { + label: string; + versions: { + [versionKey: string]: VersionInfo, + current: VersionInfo + }; + nodes?: TocNodeCollection; +} + +export type TocNodeCollection = { [tocNodeKey: string]: TocNode }; + + +export type TocTreeNode = { + id: string; + label: string; + uri: string; + nodes?: TocTreeNode[]; + disabled?: boolean; +} diff --git a/sdnr/wt/odlux/apps/helpApp/src/plugin.tsx b/sdnr/wt/odlux/apps/helpApp/src/plugin.tsx new file mode 100644 index 000000000..50a264b15 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src/plugin.tsx @@ -0,0 +1,91 @@ +/** + * ============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 * as React from "react"; +import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; + +import { faFirstAid } from '@fortawesome/free-solid-svg-icons'; // select app icon + +import applicationManager from '../../../framework/src/services/applicationManager'; +import { IApplicationStoreState } from "../../../framework/src/store/applicationStore"; +import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect'; + +import { requestTocAsyncAction, requestDocumentAsyncActionCreator } from "./actions/helpActions"; +import { helpAppRootHandler } from './handlers/helpAppRootHandler'; + +import { HelpApplication } from './views/helpApplication'; +import { HelpStatus } from "./components/helpStatus"; + +import '!style-loader!css-loader!highlight.js/styles/default.css'; +import HelpTocApp from "./views/helpTocApp"; + +const mapProps = (state: IApplicationStoreState) => ({ + +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + requestDocument: (path: string) => { + dispatcher.dispatch(requestDocumentAsyncActionCreator(path)); + } +}); + +let currentHelpPath: string | undefined = undefined; + +const HelpApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ '0'?: string }> & Connect<typeof mapProps, typeof mapDisp>) => { + + if (currentHelpPath !== props.match.params["0"]) { + // route parameter has changed + currentHelpPath = props.match.params["0"] || undefined; + // Hint: This timeout is need, since it is not recommended to change the state while rendering is in progress ! + window.setTimeout(() => { + if (currentHelpPath) { + props.requestDocument(currentHelpPath); + } + }); + } + + return ( + <HelpApplication /> + ) +}); + +const App = withRouter((props: RouteComponentProps) => ( + <Switch> + <Route exact path={`${props.match.path}/`} component={HelpTocApp} /> + <Route path={`${props.match.path}/*`} component={HelpApplicationRouteAdapter} /> + <Route path={`${props.match.path}`} component={HelpTocApp} /> + </Switch> +)); + +export async function register() { + const applicationApi = applicationManager.registerApplication({ + name: "help", + icon: faFirstAid, + rootComponent: App, + rootActionHandler: helpAppRootHandler, + statusBarElement: HelpStatus, + menuEntry: "Help", + //subMenuEntry: SubMenuEntry + }); + + // start the initial toc request after the application store is initalized + const store = await applicationApi.applicationStoreInitialized; + store.dispatch(requestTocAsyncAction); + +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/helpApp/src/services/helpService.ts b/sdnr/wt/odlux/apps/helpApp/src/services/helpService.ts new file mode 100644 index 000000000..c8b247832 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src/services/helpService.ts @@ -0,0 +1,65 @@ +/** + * ============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 { requestRest } from '../../../../framework/src/services/restService'; +import { TocTreeNode, TocNodeCollection } from '../models/tocNode'; + +class HelpService { + + private tocNodeCollection: TocTreeNode[] | null = null; + private documents: { [path: string]: string | null } = {}; + + public async getDocument(path: string): Promise<string | null> { + // check if the result is allready in the cache + if (this.documents[path]) return Promise.resolve(this.documents[path]); + + // request the document + const result = await requestRest<string>(`/help/${path}`.replace(/\/{2,}/i, '/')); + if (result) { + this.documents[path] = result; + } + return this.documents[path] || null; + } + + public async getTableOfContents(): Promise<TocTreeNode[] | null> { + // check if the result is allready in the cache + if (this.tocNodeCollection) return Promise.resolve(this.tocNodeCollection); + + // request the table of contents + const result = await requestRest<TocNodeCollection>('/help/?meta', undefined, false); + if (result !== null) { + const mapNodesCollection = (col: TocNodeCollection): TocTreeNode[] => { + return Object.keys(col).reduce<TocTreeNode[]>((acc, key) => { + const current = col[key]; + acc.push({ + id: key, + label: current.label, + uri: current.versions.current.path, + nodes: current.nodes && mapNodesCollection(current.nodes) || undefined + }); + return acc; + }, []); + } + + this.tocNodeCollection = result && mapNodesCollection(result); + } + return this.tocNodeCollection || null; + } +} + +export const helpService = new HelpService(); +export default helpService;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/helpApp/src/utilities/path.ts b/sdnr/wt/odlux/apps/helpApp/src/utilities/path.ts new file mode 100644 index 000000000..412bdfb1e --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src/utilities/path.ts @@ -0,0 +1,79 @@ +/** + * ============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 const resolvePath = (...paths: string[]): string => { + function resolve(pathA: string, pathB: string) { + // ‘a’ => ['a'] + // 'a/b' => ['a', 'b'] + // '/a/b' => ['', 'a', 'b'] + // '/a/b/' => ['', 'a', 'b', ''] + const pathBParts = pathB.split('/'); + if (pathBParts[0] === '') { + return pathBParts.join('/'); + } + const pathAParts = pathA.split('/'); + const aLastIndex = pathAParts.length - 1; + if (pathAParts[aLastIndex] !== '') { + pathAParts[aLastIndex] = ''; + } + + let part: string; + let i = 0; + while (typeof (part = pathBParts[i]) === 'string') { + switch (part) { + case '..': + pathAParts.pop(); + pathAParts.pop(); + pathAParts.push(''); + break; + case '.': + pathAParts.pop(); + pathAParts.push(''); + break; + default: + pathAParts.pop(); + pathAParts.push(part); + pathAParts.push(''); + break; + } + i++; + } + if (pathBParts[pathBParts.length - 1] !== '') pathAParts.pop(); + return pathAParts.join('/'); + } + + let i = 0; + let path: string; + let r = location.pathname; + + const urlRegex = /^https?\:\/\/([^\/?#]+)(?:[\/?#]|$)/i; + const multiSlashReg = /\/\/+/g; + + while (typeof (path = paths[i]) === 'string') { + debugger; + const matches = path && path.match(urlRegex); + if (matches || !i) { + r = path; + } else { + path = path.replace(multiSlashReg, '/'); + r = resolve(r, path); + } + i++; + } + + return r; +};
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx b/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx new file mode 100644 index 000000000..b4de26d50 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx @@ -0,0 +1,70 @@ +/** + * ============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 * as marked from 'marked'; + +import { resolvePath } from '../utilities/path'; + +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import connect, { Connect } from '../../../../framework/src/flux/connect'; + +import { Markdown } from "../components/markdown"; + +import '!style-loader!css-loader!github-markdown-css/github-markdown.css' + +const mapProps = (state: IApplicationStoreState) => ({ + content: state.help.content, + currentPath: state.help.currentPath +}); + +type HelpApplicationComponentProps = Connect<typeof mapProps>; + +class HelpApplicationComponent extends React.Component<HelpApplicationComponentProps> { + + /** + * Initializes a new instance. + */ + constructor(props: HelpApplicationComponentProps) { + super(props); + + this.renderer = new marked.Renderer(); + + this.renderer.link = (href: string, title: string, text: string) => { + // check if href is rel or abs + const absUrlMatch = href.trim().match(/^https?:\/\//i); + return `<a href="${absUrlMatch ? href : resolvePath('#/help/', this.props.currentPath || '/', href)}" title="${title}" >${text}</a>` + }; + + this.renderer.image = (href: string, title: string) => { + return `<img src="${resolvePath('/help/', this.props.currentPath || '/', href)}" alt="${title}" />` + }; + + } + + render(): JSX.Element { + return this.props.content ? ( + <Markdown text={this.props.content} markedOptions={{ renderer: this.renderer }} className="markdown-body" + style={{ maxWidth: "960px", margin: "1.5em auto" }} /> + ) : (<h2>Loading ...</h2>) + } + + private renderer: marked.Renderer; +} + +export const HelpApplication = connect(mapProps)(HelpApplicationComponent); +export default HelpApplication;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/helpApp/src/views/helpTocApp.tsx b/sdnr/wt/odlux/apps/helpApp/src/views/helpTocApp.tsx new file mode 100644 index 000000000..65d4609e2 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src/views/helpTocApp.tsx @@ -0,0 +1,55 @@ +/** + * ============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 connect, { Connect, IDispatcher } from "../../../../framework/src/flux/connect"; + +import { NavigateToApplication } from "../../../../framework/src/actions/navigationActions"; +import * as React from 'react' +import { FunctionComponent } from "react"; +import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore"; +import TocEntry from "../components/tocEntry"; +import { Typography } from "@material-ui/core"; + +const mapProps = (state: IApplicationStoreState) => ({ + helpToc: state.help.toc, +}) + +const mapDisp = (dispatcher: IDispatcher) => ({ + requestDocument: (uri: string) => dispatcher.dispatch(new NavigateToApplication("help", uri)) +}); + +const HelpTocComponent: FunctionComponent<Connect<typeof mapProps, typeof mapDisp>> = (props) => { + + return ( + <div> + <Typography style={{ marginBottom: '30px' }} variant="h5"> + Help & FAQ + </Typography> + <Typography style={{ marginBottom: '30px' }} variant="body1"> + On our Help site, you can find general information about SDN-R, detailed information about our applications, frequently asked questions and a list of used abbreviations. + </Typography> + { + props.helpToc && props.helpToc.map((item, index) => <TocEntry key={index} overviewUri={item.uri} nodes={item.nodes} label={item.label} loadDocument={props.requestDocument} />) + } + </div> + ) +} + +export const HelpTocApp = connect(mapProps, mapDisp)(HelpTocComponent) + +export default HelpTocApp;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/helpApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java b/sdnr/wt/odlux/apps/helpApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java new file mode 100644 index 000000000..1e882fc69 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles; + +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class MyOdluxBundle extends OdluxBundle { + + @Override + public void initialize() { + super.initialize(); + } + + @Override + public void clean() { + super.clean(); + } + + @Override + public String getResourceFileContent(String filename) { + return super.getResourceFileContent(filename); + } + + @Override + public boolean hasResource(String filename) { + return super.hasResource(filename); + } + + @Override + public void setBundleName(String bundleName) { + super.setBundleName(bundleName); + } + + @Override + public void setLoader(OdluxBundleLoader loader) { + super.setLoader(loader); + } + + @Override + public String getBundleName() { + return super.getBundleName(); + } + + @Override + public OdluxBundleLoader getLoader() { + return super.getLoader(); + } + + public MyOdluxBundle() { + super(); + } +} diff --git a/sdnr/wt/odlux/apps/helpApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml b/sdnr/wt/odlux/apps/helpApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..a0e3dac11 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,9 @@ +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <reference id="loadersvc" availability="mandatory" activation="eager" interface="org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader"/> + + <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle"> + <property name="loader" ref="loadersvc"/> + <property name="bundleName" value="helpApp"/> + <property name="index" value="100"/> + </bean> +</blueprint>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/helpApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java b/sdnr/wt/odlux/apps/helpApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java new file mode 100644 index 000000000..d0fbe63c0 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.onap.ccsdk.features.sdnr.wt.odlux.OdluxBundleLoaderImpl; +import org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle; + +public class TestBundleRes { + + @Test + public void test() { + OdluxBundleLoaderImpl loader = OdluxBundleLoaderImpl.getInstance(); + MyOdluxBundle b = new MyOdluxBundle(); + b.setLoader(loader); + b.setIndex(0); + b.setBundleName("abc"); + b.initialize(); + assertTrue(loader.getNumberOfBundles()==1); + assertNotNull(b.getLoader()); + assertEquals("abc",b.getBundleName()); + assertTrue(b.hasResource("test.js")); + assertNotNull(b.getResourceFileContent("test.js")); + b.clean(); + assertTrue(loader.getNumberOfBundles()==0); + } + +} diff --git a/sdnr/wt/odlux/apps/helpApp/src2/test/resources/test.js b/sdnr/wt/odlux/apps/helpApp/src2/test/resources/test.js new file mode 100644 index 000000000..b47fdc39f --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src2/test/resources/test.js @@ -0,0 +1,5 @@ +asdac sad +as +d +sad + sadfa
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/helpApp/tsconfig.json b/sdnr/wt/odlux/apps/helpApp/tsconfig.json new file mode 100644 index 000000000..a66b5d828 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": false, + "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": [ + "prop-types", + "react", + "react-dom" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/sdnr/wt/odlux/apps/helpApp/webpack.config.js b/sdnr/wt/odlux/apps/helpApp/webpack.config.js new file mode 100644 index 000000000..14ab528a7 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/webpack.config.js @@ -0,0 +1,170 @@ +/** + * 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 autoprefixer = require('autoprefixer'); +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: { + helpApp: ["./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" + }] + }, { + test: /\.css$/, + use: [{ + loader: 'style-loader' + }, { + loader: 'css-loader', + options: { + modules: true, + localIdentName: env !== "release" ? '[name]_[local]_[hash:base64:5]' : '[hash]' + } + }, { + loader: 'postcss-loader', + options: { + plugins: () => [autoprefixer] + } + }] + }] + }, + + 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://10.20.6.29:48181", + secure: false + }, + "/database/": { + target: "http://10.20.6.29:48181", + secure: false + }, + "/restconf/": { + target: "http://10.20.6.29:48181", + secure: false + }, + "/help/": { + target: "http://10.20.6.29:48181", + secure: false + }, + "/websocket/": { + target: "http://10.20.6.29:48181", + ws: true, + changeOrigin: true, + secure: false + } + } + } + }]; +} diff --git a/sdnr/wt/odlux/apps/inventoryApp/.babelrc b/sdnr/wt/odlux/apps/inventoryApp/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/.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/apps/inventoryApp/package.json b/sdnr/wt/odlux/apps/inventoryApp/package.json new file mode 100644 index 000000000..9e004c112 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/package.json @@ -0,0 +1,40 @@ +{ + "name": "@odlux/inventory-app", + "version": "0.1.0", + "description": "A react based modular UI to display network inventory data from a database.", + "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": { + "@odlux/framework": "*" + }, + "peerDependencies": { + "@types/react": "16.9.11", + "@types/react-dom": "16.9.4", + "@types/react-router-dom": "4.3.1", + "@material-ui/core": "4.6.1", + "@material-ui/icons": "4.5.1", + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "jquery": "3.3.1", + "react": "16.11.0", + "react-dom": "16.11.0", + "react-router-dom": "4.3.1" + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/pom.xml b/sdnr/wt/odlux/apps/inventoryApp/pom.xml new file mode 100644 index 000000000..081f070fc --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/pom.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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"> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> + <artifactId>sdnr-wt-odlux-app-inventoryApp</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>sdnr-wt-odlux-app-inventoryApp</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-provider</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src2/main/java</sourceDirectory> + <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>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src2/test/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.1</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>v10.16.3</nodeVersion> + <yarnVersion>v1.19.0</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package>org.onap.ccsdk.features.sdnr.wt.odlux.model.*,com.opensymphony.*</Import-Package> + <Private-Package/> + </instructions> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + <resource> + <directory>src2/main/resources</directory> + </resource> + <resource> + <directory>src2/test/resources</directory> + </resource> + </resources> + </build> +</project> diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts new file mode 100644 index 000000000..786f6d0c5 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts @@ -0,0 +1,45 @@ +/** +* ============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'; + +// ** do not remove ** +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { IActionHandler } from '../../../../framework/src/flux/action'; +import { IInventoryElementsState, inventoryElementsActionHandler } from './inventoryElementsHandler'; + + +export interface IInventoryAppStateState { + inventoryElements: IInventoryElementsState +} + + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + inventory: IInventoryAppStateState; + } +} + +const actionHandlers = { + inventoryElements: inventoryElementsActionHandler +}; + +export const inventoryAppRootHandler = combineActionHandler<IInventoryAppStateState>(actionHandlers); +export default inventoryAppRootHandler; + diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.tsx new file mode 100644 index 000000000..a65319efa --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.tsx @@ -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 { InventoryType } from '../models/inventory'; + +export interface IInventoryElementsState extends IExternalTableState<InventoryType> { } + +// create eleactic search material data fetch handler +const inventoryElementsSearchHandler = createSearchDataHandler<InventoryType>("inventory"); + +export const { + actionHandler: inventoryElementsActionHandler, + createActions: createInventoryElementsActions, + createProperties: createInventoryElementsProperties, + reloadAction: inventoryElementsReloadAction, + + // set value action, to change a value +} = createExternal<InventoryType>(inventoryElementsSearchHandler, appState => appState.inventory.inventoryElements); + diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/index.html b/sdnr/wt/odlux/apps/inventoryApp/src/index.html new file mode 100644 index 000000000..0cdb9e93b --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/index.html @@ -0,0 +1,25 @@ +<!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>Inventory 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", "inventoryApp"], function (app, inventoryApp) { + inventoryApp.register(); + app("./app.tsx").runApplication(); + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts b/sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts new file mode 100644 index 000000000..9d747415f --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts @@ -0,0 +1,34 @@ +/** + * ============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 { HitEntry, Result } from '../../../../framework/src/models'; +export type InventoryType = { + treeLevel: number; + parentUuid: string; + nodeId: string; + uuid: string; + containedHolder?: (string)[] | null; + manufacturerName?: string ; + manufacturerIdentifier: string; + serial: string; + date: string; + version: string; + description: string; + partTypeId: string; + modelIdentifier: string; + typeName: string; +} diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx new file mode 100644 index 000000000..ad53285cb --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx @@ -0,0 +1,75 @@ +/** +* ============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 * as React from "react"; +import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; +import { faShoppingBag } from '@fortawesome/free-solid-svg-icons'; // select app icon +import applicationManager from '../../../framework/src/services/applicationManager'; + +import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect'; +import { IApplicationStoreState } from "../../../framework/src/store/applicationStore"; + +import { Dashboard } from './views/dashboard'; +import inventoryAppRootHandler from './handlers/inventoryAppRootHandler'; + +import { createInventoryElementsProperties, createInventoryElementsActions, inventoryElementsReloadAction } from "./handlers/inventoryElementsHandler"; + +let currentMountId: string | undefined = undefined; + +const mapProps = (state: IApplicationStoreState) => ({ + inventoryProperties: createInventoryElementsProperties(state), +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + inventoryActions: createInventoryElementsActions(dispatcher.dispatch, true) +}); + +const InventoryApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ mountId?: string }> & Connect<typeof mapProps, typeof mapDisp>) => { + if (currentMountId !== props.match.params.mountId) { + currentMountId = props.match.params.mountId || undefined; + window.setTimeout(() => { + if (currentMountId) { + props.inventoryActions.onFilterChanged("nodeId", currentMountId); + props.inventoryProperties.showFilter; + props.inventoryActions.onRefresh(); + } + }); + } + return ( + <Dashboard /> + ) +}); + +const App = withRouter((props: RouteComponentProps) => ( + <Switch> + <Route path={`${props.match.path}/:mountId?`} component={InventoryApplicationRouteAdapter} /> + <Redirect to={`${props.match.path}`} /> + </Switch> +)); + +export function register() { + applicationManager.registerApplication({ + name: "inventory", + icon: faShoppingBag, + rootActionHandler: inventoryAppRootHandler, + rootComponent: App, + menuEntry: "Inventory" + }); +} + diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx new file mode 100644 index 000000000..bd182ed5d --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx @@ -0,0 +1,64 @@ +/** + * ============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 { Connect, connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; + +import { InventoryType } from '../models/inventory'; +import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore"; +import { createInventoryElementsProperties, createInventoryElementsActions } from "../handlers/inventoryElementsHandler"; + +const InventoryTable = MaterialTable as MaterialTableCtorType<InventoryType & {_id: string}>; + +const mapProps = (state: IApplicationStoreState) => ({ + inventoryElementsProperties: createInventoryElementsProperties(state), + inventoryElements: state.inventory.inventoryElements +}); + +const mapDispatch = (dispatcher: IDispatcher) => ({ + inventoryElementsActions: createInventoryElementsActions(dispatcher.dispatch) +}); + +class DashboardComponent extends React.Component<Connect<typeof mapProps, typeof mapDispatch>> { + render() { + return <InventoryTable title="Inventory" idProperty="_id" columns={[ + { property: "nodeId", title: "Node Name" }, + { property: "manufacturerIdentifier", title: "Manufacturer" }, + { property: "parentUuid", title: "Parent" }, + { property: "uuid", title: "Name" }, + { property: "serial", title: "Serial" }, + { property: "version", title: "Version" }, + { property: "date", title: "Date" }, + { property: "description", title: "Description" }, + { property: "partTypeId", title: "Part Type Id" }, + { property: "modelIdentifier", title: "Model Identifier" }, + { property: "typeName", title: "Type" }, + { property: "treeLevel", title: "Containment Level" }, + ]} {...this.props.inventoryElementsActions} {...this.props.inventoryElementsProperties} > + </InventoryTable> + } + + componentDidMount() { + this.props.inventoryElementsActions.onToggleFilter(); + this.props.inventoryElementsActions.onHandleRequestSort("node-id"); + } +} + +export const Dashboard = connect(mapProps, mapDispatch)(DashboardComponent); +export default Dashboard;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/views/detail.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/views/detail.tsx new file mode 100644 index 000000000..f03b30cdd --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/views/detail.tsx @@ -0,0 +1,41 @@ +/** + * ============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 { withRouter, RouteComponentProps } from 'react-router-dom'; + +import Button from '@material-ui/core/Button'; +import { WithStyles, withStyles, createStyles, Theme } from '@material-ui/core/styles'; // infra for styling + +const styles = (theme: Theme) => createStyles({ + warnButton: { + backgroundColor: theme.palette.primary.dark + } +}); + +type DetailProps = RouteComponentProps<{ id: string }> & WithStyles<typeof styles>; + +export const Detail = withStyles( styles )( withRouter( (props: DetailProps) => ( + <div> + <h1>Detail {props.match.params.id}</h1> + <p>This are the information about {props.staticContext}.</p> + <Button color={"secondary"} variant={"contained"}>Start</Button> + <Button className={ props.classes.warnButton } variant={"contained"}>Stop</Button> + </div> +))); + +export default Detail;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java b/sdnr/wt/odlux/apps/inventoryApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java new file mode 100644 index 000000000..1e882fc69 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles; + +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class MyOdluxBundle extends OdluxBundle { + + @Override + public void initialize() { + super.initialize(); + } + + @Override + public void clean() { + super.clean(); + } + + @Override + public String getResourceFileContent(String filename) { + return super.getResourceFileContent(filename); + } + + @Override + public boolean hasResource(String filename) { + return super.hasResource(filename); + } + + @Override + public void setBundleName(String bundleName) { + super.setBundleName(bundleName); + } + + @Override + public void setLoader(OdluxBundleLoader loader) { + super.setLoader(loader); + } + + @Override + public String getBundleName() { + return super.getBundleName(); + } + + @Override + public OdluxBundleLoader getLoader() { + return super.getLoader(); + } + + public MyOdluxBundle() { + super(); + } +} diff --git a/sdnr/wt/odlux/apps/inventoryApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml b/sdnr/wt/odlux/apps/inventoryApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..f0bf05483 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,9 @@ +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <reference id="loadersvc" availability="mandatory" activation="eager" interface="org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader"/> + + <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle"> + <property name="loader" ref="loadersvc"/> + <property name="bundleName" value="inventoryApp"/> + <property name="index" value="70"/> + </bean> +</blueprint>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java b/sdnr/wt/odlux/apps/inventoryApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java new file mode 100644 index 000000000..d0fbe63c0 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.onap.ccsdk.features.sdnr.wt.odlux.OdluxBundleLoaderImpl; +import org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle; + +public class TestBundleRes { + + @Test + public void test() { + OdluxBundleLoaderImpl loader = OdluxBundleLoaderImpl.getInstance(); + MyOdluxBundle b = new MyOdluxBundle(); + b.setLoader(loader); + b.setIndex(0); + b.setBundleName("abc"); + b.initialize(); + assertTrue(loader.getNumberOfBundles()==1); + assertNotNull(b.getLoader()); + assertEquals("abc",b.getBundleName()); + assertTrue(b.hasResource("test.js")); + assertNotNull(b.getResourceFileContent("test.js")); + b.clean(); + assertTrue(loader.getNumberOfBundles()==0); + } + +} diff --git a/sdnr/wt/odlux/apps/inventoryApp/src2/test/resources/test.js b/sdnr/wt/odlux/apps/inventoryApp/src2/test/resources/test.js new file mode 100644 index 000000000..b47fdc39f --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src2/test/resources/test.js @@ -0,0 +1,5 @@ +asdac sad +as +d +sad + sadfa
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/tsconfig.json b/sdnr/wt/odlux/apps/inventoryApp/tsconfig.json new file mode 100644 index 000000000..a66b5d828 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": false, + "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": [ + "prop-types", + "react", + "react-dom" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js b/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js new file mode 100644 index 000000000..426763803 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js @@ -0,0 +1,156 @@ +/** + * 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: { + inventoryApp: ["./pluginInventory.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" + }] + }] + }, + + 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:48181", + secure: false + }, + "/database/": { + target: "http://localhost:48181", + secure: false + }, + "/restconf/": { + target: "http://localhost:48181", + secure: false + }, + "/help/": { + target: "http://localhost:48181", + secure: false + }, + "/websocket/": { + target: "http://localhost:48181", + ws: true, + changeOrigin: true, + secure: false + } + } + + } + }]; +} diff --git a/sdnr/wt/odlux/apps/maintenanceApp/.babelrc b/sdnr/wt/odlux/apps/maintenanceApp/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/.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/apps/maintenanceApp/package.json b/sdnr/wt/odlux/apps/maintenanceApp/package.json new file mode 100644 index 000000000..96f9f7d2b --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/package.json @@ -0,0 +1,41 @@ +{ + "name": "@odlux/maintenance-app", + "version": "0.1.0", + "description": "A react based modular UI for the maintenance 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": { + "@odlux/framework": "*", + "@odlux/connect-app": "*" + }, + "peerDependencies": { + "@types/react": "16.9.11", + "@types/react-dom": "16.9.4", + "@types/react-router-dom": "4.3.1", + "@material-ui/core": "4.6.1", + "@material-ui/icons": "4.5.1", + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "jquery": "3.3.1", + "react": "16.11.0", + "react-dom": "16.11.0", + "react-router-dom": "4.3.1" + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/maintenanceApp/pom.xml b/sdnr/wt/odlux/apps/maintenanceApp/pom.xml new file mode 100644 index 000000000..be6e2ea6e --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/pom.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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"> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> + <artifactId>sdnr-wt-odlux-app-maintenanceApp</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>sdnr-wt-odlux-app-maintenanceApp</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-provider</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src2/main/java</sourceDirectory> + <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>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src2/test/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.1</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>v10.16.3</nodeVersion> + <yarnVersion>v1.19.0</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package>org.onap.ccsdk.features.sdnr.wt.odlux.model.*,com.opensymphony.*</Import-Package> + <Private-Package/> + </instructions> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + <resource> + <directory>src2/main/resources</directory> + </resource> + <resource> + <directory>src2/test/resources</directory> + </resource> + </resources> + </build> +</project> diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts new file mode 100644 index 000000000..162d94367 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts @@ -0,0 +1,79 @@ +/** + * ============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 { MaintenenceEntry, spoofSymbol } from '../models/maintenenceEntryType'; + +import { AddSnackbarNotification } from '../../../../framework/src/actions/snackbarActions'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + + +import { maintenenceService } from '../services/maintenenceService'; +import { maintenanceEntriesReloadAction } from '../handlers/maintenenceEntriesHandler'; + +export class BaseAction extends Action { } + +export class LoadAllMainteneceEntriesAction extends BaseAction { } + +export class AllMainteneceEntriesLoadedAction extends BaseAction { + + constructor (public maintenenceEntries: MaintenenceEntry[] | null, error?:string) { + super(); + + } +} + + +export class UpdateMaintenanceEntry extends BaseAction { + constructor(public maintenenceEntry: MaintenenceEntry) { + super(); + } +} + +/** Represents an async thunk action creator to add an element to the maintenence entries. */ +export const addOrUpdateMaintenenceEntryAsyncActionCreator = (entry: MaintenenceEntry) => (dispatch: Dispatch) => { + maintenenceService.writeMaintenenceEntry(entry).then(result => { + result && window.setTimeout(() => { + // dispatch(loadAllMountedNetworkElementsAsync); + dispatch(new UpdateMaintenanceEntry(entry)); + dispatch(new AddSnackbarNotification({ message: `Successfully ${result && result.created ? "created" : "updated"} maintenance settings for [${entry.nodeId}]`, options: { variant: 'success' } })); + }, 900); + dispatch(maintenanceEntriesReloadAction) + }); +}; + +/** Represents an async thunk action creator to delete an element from the maintenence entries. */ +export const removeFromMaintenenceEntrysAsyncActionCreator = (entry: MaintenenceEntry) => (dispatch: Dispatch) => { + maintenenceService.deleteMaintenenceEntry(entry).then(result => { + result && window.setTimeout(() => { + dispatch(new UpdateMaintenanceEntry({ + [spoofSymbol]: true, + _id: entry._id, + nodeId: entry.nodeId, + description: "", + start: "", + end: "", + active: false + })); + dispatch(new AddSnackbarNotification({ message: `Successfully removed [${entry.nodeId}]`, options: { variant: 'success' } })); + }, 900); + dispatch(maintenanceEntriesReloadAction) + }); +}; + +// Hint: since there is no notification of changed required network elements, this code is not aware of changes caused outiside of this browser.
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx b/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx new file mode 100644 index 000000000..e0cd51493 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx @@ -0,0 +1,194 @@ +/** + * ============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 '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; + +import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; + +import { + addOrUpdateMaintenenceEntryAsyncActionCreator, + removeFromMaintenenceEntrysAsyncActionCreator +} from '../actions/maintenenceActions'; + +import { MaintenenceEntry } from '../models/maintenenceEntryType'; +import { FormControl, InputLabel, Select, MenuItem } from '@material-ui/core'; + +export enum EditMaintenenceEntryDialogMode { + None = "none", + AddMaintenenceEntry = "addMaintenenceEntry", + EditMaintenenceEntry = "editMaintenenceEntry", + RemoveMaintenenceEntry = "removeMaintenenceEntry" +} + +const mapDispatch = (dispatcher: IDispatcher) => ({ + addOrUpdateMaintenenceEntry: (entry: MaintenenceEntry) => { + dispatcher.dispatch(addOrUpdateMaintenenceEntryAsyncActionCreator(entry)); + }, + removeMaintenenceEntry: (entry: MaintenenceEntry) => { + dispatcher.dispatch(removeFromMaintenenceEntrysAsyncActionCreator(entry)); + }, +}); + +type DialogSettings = { + dialogTitle: string, + dialogDescription: string, + applyButtonText: string, + cancelButtonText: string, + enableMountIdEditor: boolean, + enableTimeEditor: boolean, +} + +const settings: { [key: string]: DialogSettings } = { + [EditMaintenenceEntryDialogMode.None]: { + dialogTitle: "", + dialogDescription: "", + applyButtonText: "", + cancelButtonText: "", + enableMountIdEditor: false, + enableTimeEditor: false, + }, + [EditMaintenenceEntryDialogMode.AddMaintenenceEntry]: { + dialogTitle: "Add new maintenence entry", + dialogDescription: "", + applyButtonText: "Add", + cancelButtonText: "Cancel", + enableMountIdEditor: true, + enableTimeEditor: true, + }, + [EditMaintenenceEntryDialogMode.EditMaintenenceEntry]: { + dialogTitle: "Edit maintenence entry", + dialogDescription: "", + applyButtonText: "Save", + cancelButtonText: "Cancel", + enableMountIdEditor: false, + enableTimeEditor: true, + }, + [EditMaintenenceEntryDialogMode.RemoveMaintenenceEntry]: { + dialogTitle: "Remove maintenence entry", + dialogDescription: "", + applyButtonText: "Remove", + cancelButtonText: "Cancel", + enableMountIdEditor: false, + enableTimeEditor: false, + }, +} + +type EditMaintenenceEntryDIalogComponentProps = Connect<undefined, typeof mapDispatch> & { + mode: EditMaintenenceEntryDialogMode; + initialMaintenenceEntry: MaintenenceEntry; + onClose: () => void; +}; + +type EditMaintenenceEntryDIalogComponentState = MaintenenceEntry; + +class EditMaintenenceEntryDIalogComponent extends React.Component<EditMaintenenceEntryDIalogComponentProps, EditMaintenenceEntryDIalogComponentState> { + constructor (props: EditMaintenenceEntryDIalogComponentProps) { + super(props); + + this.state = { + ...this.props.initialMaintenenceEntry + }; + } + + render(): JSX.Element { + const setting = settings[this.props.mode]; + return ( + <Dialog open={this.props.mode !== EditMaintenenceEntryDialogMode.None}> + <DialogTitle id="form-dialog-title">{setting.dialogTitle}</DialogTitle> + <DialogContent> + <DialogContentText> + {setting.dialogDescription} + </DialogContentText> + <TextField disabled={!setting.enableMountIdEditor} spellCheck={false} autoFocus margin="dense" id="name" label="Name" type="text" fullWidth value={this.state.nodeId} onChange={(event) => { this.setState({ nodeId: event.target.value }); }} /> + <TextField disabled={!setting.enableTimeEditor} spellCheck={false} autoFocus margin="dense" id="start" label="Start (Local DateTime)" type="datetime-local" fullWidth value={this.state.start} onChange={(event) => { this.setState({ start: event.target.value }); }} /> + <TextField disabled={!setting.enableTimeEditor} spellCheck={false} autoFocus margin="dense" id="end" label="End (Local DateTime)" type="datetime-local" fullWidth value={this.state.end} onChange={(event) => { this.setState({ end: event.target.value }); }} /> + <FormControl fullWidth disabled={!setting.enableTimeEditor}> + <InputLabel htmlFor="active">Active</InputLabel> + <Select value={ this.state.active || false } onChange={(event) => { + this.setState({ active: event.target.value as any as boolean }); + }} inputProps={{ name: 'active', id: 'active' }} fullWidth > + <MenuItem value={true as any as string}>active</MenuItem> + <MenuItem value={false as any as string}>not active</MenuItem> + </Select> + </FormControl> + </DialogContent> + <DialogActions> + <Button onClick={(event) => { + this.onApply({ + _id: this.state._id || this.state.nodeId, + nodeId: this.state.nodeId, + description: this.state.description, + start: this.state.start, + end: this.state.end, + active: this.state.active + }); + event.preventDefault(); + event.stopPropagation(); + }} > {setting.applyButtonText} </Button> + <Button onClick={(event) => { + this.onCancel(); + event.preventDefault(); + event.stopPropagation(); + }} color="secondary"> {setting.cancelButtonText} </Button> + </DialogActions> + </Dialog> + ) + } + + private onApply = (entry: MaintenenceEntry) => { + this.props.onClose && this.props.onClose(); + switch (this.props.mode) { + case EditMaintenenceEntryDialogMode.AddMaintenenceEntry: + entry && this.props.addOrUpdateMaintenenceEntry(entry); + break; + case EditMaintenenceEntryDialogMode.EditMaintenenceEntry: + entry && this.props.addOrUpdateMaintenenceEntry(entry); + break; + case EditMaintenenceEntryDialogMode.RemoveMaintenenceEntry: + entry && this.props.removeMaintenenceEntry(entry); + break; + } + }; + + + private onCancel = () => { + this.props.onClose && this.props.onClose(); + } + + static getDerivedStateFromProps(props: EditMaintenenceEntryDIalogComponentProps, state: EditMaintenenceEntryDIalogComponentState & { _initialMaintenenceEntry: MaintenenceEntry }): EditMaintenenceEntryDIalogComponentState & { _initialMaintenenceEntry: MaintenenceEntry } { + if (props.initialMaintenenceEntry !== state._initialMaintenenceEntry) { + state = { + ...state, + ...props.initialMaintenenceEntry, + _initialMaintenenceEntry: props.initialMaintenenceEntry, + }; + } + return state; + } + +} + +export const EditMaintenenceEntryDIalog = connect(undefined, mapDispatch)(EditMaintenenceEntryDIalogComponent); +export default EditMaintenenceEntryDIalog;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts new file mode 100644 index 000000000..71b4bf70d --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceAppRootHandler.ts @@ -0,0 +1,41 @@ +/** + * ============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 { IMaintenanceEntriesState, maintenanceEntriesActionHandler } from './maintenenceEntriesHandler'; + +export interface IMaintenanceAppStoreState { + maintenenceEntries : IMaintenanceEntriesState +} + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + maintenance: IMaintenanceAppStoreState + } +} + +const actionHandlers = { + maintenenceEntries: maintenanceEntriesActionHandler +}; + +export const maintenanceAppRootHandler = combineActionHandler<IMaintenanceAppStoreState>(actionHandlers); +export default maintenanceAppRootHandler; diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenenceEntriesHandler.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenenceEntriesHandler.ts new file mode 100644 index 000000000..445541889 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenenceEntriesHandler.ts @@ -0,0 +1,35 @@ +/** + * ============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 { MaintenenceEntry } from '../models/maintenenceEntryType'; +export interface IMaintenanceEntriesState extends IExternalTableState<MaintenenceEntry> { } + +// create eleactic search material data fetch handler +const maintenanceEntriesSearchHandler = createSearchDataHandler<MaintenenceEntry>('maintenance'); + +export const { + actionHandler: maintenanceEntriesActionHandler, + createActions: createmaintenanceEntriesActions, + createProperties: createmaintenanceEntriesProperties, + reloadAction: maintenanceEntriesReloadAction, + + // set value action, to change a value +} = createExternal<MaintenenceEntry>(maintenanceEntriesSearchHandler, appState => appState.maintenance.maintenenceEntries); + diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/index.html b/sdnr/wt/odlux/apps/maintenanceApp/src/index.html new file mode 100644 index 000000000..c84aecee1 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/index.html @@ -0,0 +1,26 @@ +<!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>Minimal 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","connectApp", "maintenanceApp"], function (app, connectApp, maintenanceApp) { + connectApp.register(); + maintenanceApp.register(); + app("./app.tsx").runApplication(); + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenenceEntryType.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenenceEntryType.ts new file mode 100644 index 000000000..989bfbdc3 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenenceEntryType.ts @@ -0,0 +1,33 @@ +/** + * ============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========================================================================== + */ +/** Represents the elestic search db type for maintenence enrties */ + + +export const spoofSymbol = Symbol("Spoof"); + +/** Represents the type for an maintenence entry. */ +export type MaintenenceEntry = { + _id: string; + nodeId: string; + description?: string, + end: string, + start: string + active: boolean + [spoofSymbol]?: boolean; +} + diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx b/sdnr/wt/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx new file mode 100644 index 000000000..e6ab977b3 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/pluginMaintenance.tsx @@ -0,0 +1,44 @@ +/** + * ============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 * as React from "react"; + +import { faLock } from '@fortawesome/free-solid-svg-icons'; // select app icon + +import applicationManager from '../../../framework/src/services/applicationManager'; + +import { maintenanceAppRootHandler } from './handlers/maintenanceAppRootHandler'; + +import MaintenenceView from "./views/maintenenceView"; + +const App : React.SFC = (props) => { + return <MaintenenceView /> +}; + +export function register() { + applicationManager.registerApplication({ + name: "maintenance", + icon: faLock, + rootComponent: App, + rootActionHandler: maintenanceAppRootHandler, + menuEntry: "Maintenance" + }); +} + + diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/services/maintenenceService.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/services/maintenenceService.ts new file mode 100644 index 000000000..daa6082d7 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/services/maintenenceService.ts @@ -0,0 +1,72 @@ +/** + * ============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 { requestRest } from '../../../../framework/src/services/restService'; +import { Result, HitEntry, PostResponse, DeleteResponse } from '../../../../framework/src/models/elasticSearch'; + +import { MaintenenceEntry } from '../models/maintenenceEntryType'; +import { convertToLocaleString, convertToGMTString, convertToISODateString } from '../utils/timeUtils'; +import { convertPropertyNames, replaceUpperCase } from '../../../../framework/src/utilities/yangHelper'; + + +export const maintenenceEntryDatabasePath = "mwtn/maintenancemode"; + +/** + * Represents a web api accessor service for all maintenence entries related actions. + */ +class MaintenenceService { + + /** + * Adds or updates one maintenence entry to the backend. + */ + public async writeMaintenenceEntry(maintenenceEntry: MaintenenceEntry): Promise<PostResponse | null> { + const path = `/restconf/operations/data-provider:create-maintenance`; + + const query = { + "id": maintenenceEntry._id, + "node-id": maintenenceEntry.nodeId, + "active": maintenenceEntry.active, + "description": maintenenceEntry.description, + "end": convertToISODateString(maintenenceEntry.end), + "start": convertToISODateString(maintenenceEntry.start) + }; + + const result = await requestRest<PostResponse>(path, { method: "POST", body: JSON.stringify(convertPropertyNames({ input: query }, replaceUpperCase)) }); + return result || null; + } + + /** + * Deletes one maintenence entry by its mountId from the backend. + */ + public async deleteMaintenenceEntry(maintenenceEntry: MaintenenceEntry): Promise<(DeleteResponse) | null> { + const path = `/restconf/operations/data-provider:delete-maintenance`; + + const query = { + "id": maintenenceEntry._id, + "node-id": maintenenceEntry.nodeId, + "active": maintenenceEntry.active, + "description": maintenenceEntry.description, + "end": convertToISODateString(maintenenceEntry.end), + "start": convertToISODateString(maintenenceEntry.start) + }; + const result = await requestRest<DeleteResponse>(path, { method: "POST", body: JSON.stringify(convertPropertyNames({ input: query }, replaceUpperCase)) }); + return result || null; + } +} + +export const maintenenceService = new MaintenenceService(); +export default maintenenceService;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/utils/timeUtils.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/utils/timeUtils.ts new file mode 100644 index 000000000..676be1a38 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/utils/timeUtils.ts @@ -0,0 +1,45 @@ +/** + * ============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 function convertToGMTString(dateString: string): string { + const date = new Date(dateString); + const pad = (n: number) => (n < 10) ? '0' + n : n; + + return date.getUTCFullYear() + + '-' + pad(date.getUTCMonth() + 1) + + '-' + pad(date.getUTCDate()) + + 'T' + pad(date.getUTCHours()) + + ':' + pad(date.getUTCMinutes()) + + '+00:00'; +} + +export function convertToLocaleString(rawDate: string| number): string { + const date = new Date(rawDate); + const pad = (n: number) => (n < 10) ? '0' + n : n; + + return date.getFullYear() + + '-' + pad(date.getMonth() + 1) + + '-' + pad(date.getDate()) + + 'T' + pad(date.getHours()) + + ':' + pad(date.getMinutes()); +} + +export function convertToISODateString(rawDate: string| number): string { + const date = new Date(rawDate); + const displayDate = date.toISOString(); + return displayDate.replace(/\.[0-9]{2}/, '.'); +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx b/sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx new file mode 100644 index 000000000..3b7879351 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx @@ -0,0 +1,226 @@ +/** + * ============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, createStyles, WithStyles, withStyles, Tooltip } from '@material-ui/core'; + +import { faBan } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; + +import AddIcon from '@material-ui/icons/Add'; +import EditIcon from '@material-ui/icons/Edit'; +import RemoveIcon from '@material-ui/icons/RemoveCircleOutline'; + +import Button from '@material-ui/core/Button'; +import IconButton from '@material-ui/core/IconButton'; + +import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect'; +import MaterialTable, { MaterialTableCtorType, ColumnType } from '../../../../framework/src/components/material-table'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import { MaintenenceEntry, spoofSymbol } from '../models/maintenenceEntryType'; + +import EditMaintenenceEntryDialog, { EditMaintenenceEntryDialogMode } from '../components/editMaintenenceEntryDialog'; +import { convertToLocaleString } from '../utils/timeUtils'; +import { createmaintenanceEntriesActions, createmaintenanceEntriesProperties, maintenanceEntriesReloadAction } from '../handlers/maintenenceEntriesHandler'; + +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 MaintenenceEntriesTable = MaterialTable as MaterialTableCtorType<MaintenenceEntry>; + +const mapProps = (state: IApplicationStoreState) => ({ + maintenanceEntriesProperties: createmaintenanceEntriesProperties(state) +}); + +const mapDispatcher = (dispatcher: IDispatcher) => ({ + maintenanceEntriesActions: createmaintenanceEntriesActions(dispatcher.dispatch), + onLoadMaintenanceEntries: async () => { + await dispatcher.dispatch(maintenanceEntriesReloadAction) + } +}); + +const emptyMaintenenceEntry: MaintenenceEntry = { + _id: '', + nodeId: '', + description: '', + start: convertToLocaleString(new Date().valueOf()), + end: convertToLocaleString(new Date().valueOf()), + active: false, +}; + +type MaintenenceViewComponentProps = Connect<typeof mapProps, typeof mapDispatcher> & WithStyles<typeof styles> & { + +} + +type MaintenenceViewComponentState = { + maintenenceEntryToEdit: MaintenenceEntry; + maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode; +} + +class MaintenenceViewComponent extends React.Component<MaintenenceViewComponentProps, MaintenenceViewComponentState> { + + constructor(props: MaintenenceViewComponentProps) { + super(props); + + this.state = { + maintenenceEntryToEdit: emptyMaintenenceEntry, + maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.None, + }; + + } + + render() { + const { classes } = this.props; + const addMaintenenceEntryAction = { + icon: AddIcon, tooltip: 'Add', onClick: () => { + const startTime = (new Date().valueOf()); + const endTime = startTime; + this.setState({ + maintenenceEntryToEdit: { + ...emptyMaintenenceEntry, + start: convertToLocaleString(startTime), + end: convertToLocaleString(endTime), + }, + maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.AddMaintenenceEntry + }); + } + }; + const now = new Date().valueOf(); + return ( + <> + <MaintenenceEntriesTable title={"Maintenance"} customActionButtons={[addMaintenenceEntryAction]} columns={ + [ + { property: "nodeId", title: "Node Name", type: ColumnType.text }, + { + property: "notifications", title: "Notification", width: 50, align: "center", type: ColumnType.custom, customControl: ({ rowData }) => ( + rowData.active && (Date.parse(rowData.start).valueOf() <= now) && (Date.parse(rowData.end).valueOf() >= now) && <FontAwesomeIcon icon={faBan} /> || null + ) + }, + { property: "active", title: "Activation State", type: ColumnType.boolean, labels: { "true": "active", "false": "not active" }, }, + { property: "start", title: "Start Date (UTC)", type: ColumnType.text }, + { property: "end", title: "End Date (UTC)", type: ColumnType.text }, + { + property: "actions", title: "Actions", type: ColumnType.custom, customControl: ({ rowData }) => ( + <> + <div className={classes.spacer}> + <Tooltip title={"1h from now"} ><Button className={classes.button} onClick={(event) => this.onOpenPlus1hEditMaintenenceEntryDialog(event, rowData)} >+1h</Button></Tooltip> + <Tooltip title={"8h from now"} ><Button className={classes.button} onClick={(event) => this.onOpenPlus8hEditMaintenenceEntryDialog(event, rowData)} >+8h</Button></Tooltip> + </div> + <div className={classes.spacer}> + <Tooltip title={"Edit"} ><IconButton className={classes.button} onClick={(event) => this.onOpenEditMaintenenceEntryDialog(event, rowData)} ><EditIcon /></IconButton></Tooltip> + <Tooltip title={"Remove"} ><IconButton disabled={!!rowData[spoofSymbol]} className={classes.button} onClick={(event) => this.onOpenRemoveMaintenenceEntryDialog(event, rowData)} ><RemoveIcon /></IconButton></Tooltip> + </div> + </> + ) + }, + ] + } idProperty={'_id'}{...this.props.maintenanceEntriesActions} {...this.props.maintenanceEntriesProperties} asynchronus > </MaintenenceEntriesTable> + <EditMaintenenceEntryDialog initialMaintenenceEntry={this.state.maintenenceEntryToEdit} mode={this.state.maintenenceEntryEditorMode} + onClose={this.onCloseEditMaintenenceEntryDialog} /> + </> + ); + } + + public componentDidMount() { + this.props.maintenanceEntriesActions.onRefresh(); + this.props.onLoadMaintenanceEntries(); + } + + private onOpenPlus1hEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenenceEntry) => { + event.preventDefault(); + event.stopPropagation(); + const startTime = (new Date().valueOf()); + const endTime = startTime + (1 * 60 * 60 * 1000); + this.setState({ + maintenenceEntryToEdit: { + ...entry, + start: convertToLocaleString(startTime), + end: convertToLocaleString(endTime), + }, + maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry + }); + } + + private onOpenPlus8hEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenenceEntry) => { + event.preventDefault(); + event.stopPropagation(); + const startTime = (new Date().valueOf()); + const endTime = startTime + (8 * 60 * 60 * 1000); + this.setState({ + maintenenceEntryToEdit: { + ...entry, + start: convertToLocaleString(startTime), + end: convertToLocaleString(endTime), + }, + maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry + }); + } + + private onOpenEditMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenenceEntry) => { + event.preventDefault(); + event.stopPropagation(); + const startTime = (new Date().valueOf()); + const endTime = startTime; + this.setState({ + maintenenceEntryToEdit: { + ...entry, + ...(entry.start && endTime) + ? { start: convertToLocaleString(entry.start), end: convertToLocaleString(entry.end) } + : { start: convertToLocaleString(startTime), end: convertToLocaleString(endTime) } + }, + maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.EditMaintenenceEntry + }); + } + + private onOpenRemoveMaintenenceEntryDialog = (event: React.MouseEvent<HTMLElement>, entry: MaintenenceEntry) => { + event.preventDefault(); + event.stopPropagation(); + const startTime = (new Date().valueOf()); + const endTime = startTime; + this.setState({ + maintenenceEntryToEdit: { + ...entry, + ...(entry.start && endTime) + ? { start: convertToLocaleString(entry.start), end: convertToLocaleString(entry.end) } + : { start: convertToLocaleString(startTime), end: convertToLocaleString(endTime) } + }, + maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.RemoveMaintenenceEntry + }); + } + + private onCloseEditMaintenenceEntryDialog = () => { + this.setState({ + maintenenceEntryToEdit: emptyMaintenenceEntry, + maintenenceEntryEditorMode: EditMaintenenceEntryDialogMode.None, + }); + } +} + +export const MaintenenceView = withStyles(styles)(connect(mapProps, mapDispatcher)(MaintenenceViewComponent)); +export default MaintenenceView;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java b/sdnr/wt/odlux/apps/maintenanceApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java new file mode 100644 index 000000000..1e882fc69 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles; + +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class MyOdluxBundle extends OdluxBundle { + + @Override + public void initialize() { + super.initialize(); + } + + @Override + public void clean() { + super.clean(); + } + + @Override + public String getResourceFileContent(String filename) { + return super.getResourceFileContent(filename); + } + + @Override + public boolean hasResource(String filename) { + return super.hasResource(filename); + } + + @Override + public void setBundleName(String bundleName) { + super.setBundleName(bundleName); + } + + @Override + public void setLoader(OdluxBundleLoader loader) { + super.setLoader(loader); + } + + @Override + public String getBundleName() { + return super.getBundleName(); + } + + @Override + public OdluxBundleLoader getLoader() { + return super.getLoader(); + } + + public MyOdluxBundle() { + super(); + } +} diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml b/sdnr/wt/odlux/apps/maintenanceApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..2dd79d781 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,9 @@ +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <reference id="loadersvc" availability="mandatory" activation="eager" interface="org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader"/> + + <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle"> + <property name="loader" ref="loadersvc"/> + <property name="bundleName" value="maintenanceApp"/> + <property name="index" value="20" /> + </bean> +</blueprint> diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java b/sdnr/wt/odlux/apps/maintenanceApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java new file mode 100644 index 000000000..d0fbe63c0 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.onap.ccsdk.features.sdnr.wt.odlux.OdluxBundleLoaderImpl; +import org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle; + +public class TestBundleRes { + + @Test + public void test() { + OdluxBundleLoaderImpl loader = OdluxBundleLoaderImpl.getInstance(); + MyOdluxBundle b = new MyOdluxBundle(); + b.setLoader(loader); + b.setIndex(0); + b.setBundleName("abc"); + b.initialize(); + assertTrue(loader.getNumberOfBundles()==1); + assertNotNull(b.getLoader()); + assertEquals("abc",b.getBundleName()); + assertTrue(b.hasResource("test.js")); + assertNotNull(b.getResourceFileContent("test.js")); + b.clean(); + assertTrue(loader.getNumberOfBundles()==0); + } + +} diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src2/test/resources/test.js b/sdnr/wt/odlux/apps/maintenanceApp/src2/test/resources/test.js new file mode 100644 index 000000000..b47fdc39f --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src2/test/resources/test.js @@ -0,0 +1,5 @@ +asdac sad +as +d +sad + sadfa
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/maintenanceApp/tsconfig.json b/sdnr/wt/odlux/apps/maintenanceApp/tsconfig.json new file mode 100644 index 000000000..a66b5d828 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": false, + "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": [ + "prop-types", + "react", + "react-dom" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/sdnr/wt/odlux/apps/maintenanceApp/webpack.config.js b/sdnr/wt/odlux/apps/maintenanceApp/webpack.config.js new file mode 100644 index 000000000..e6d968564 --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/webpack.config.js @@ -0,0 +1,154 @@ +/** + * 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: { + maintenanceApp: ["./pluginMaintenance.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" + }] + }] + }, + + 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://10.20.6.29:28181", + secure: false + }, + "/database/": { + target: "http://10.20.6.29:28181", + secure: false + }, + "/restconf/": { + target: "http://10.20.6.29:28181", + secure: false + }, + "/help/": { + target: "http://10.20.6.29:28181", + secure: false + }, + "/websocket/": { + target: "http://10.20.6.29:28181", + ws: true, + changeOrigin: true, + secure: false + } + } + } + }]; +} diff --git a/sdnr/wt/odlux/apps/mediatorApp/.babelrc b/sdnr/wt/odlux/apps/mediatorApp/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt/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/apps/mediatorApp/package.json b/sdnr/wt/odlux/apps/mediatorApp/package.json new file mode 100644 index 000000000..718d2fd03 --- /dev/null +++ b/sdnr/wt/odlux/apps/mediatorApp/package.json @@ -0,0 +1,41 @@ +{ + "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": { + "@odlux/framework": "*" + }, + "peerDependencies": { + "@fortawesome/free-solid-svg-icons": "5.6.3", + "@types/react": "16.9.11", + "@types/react-dom": "16.9.4", + "@types/react-router-dom": "4.3.1", + "@material-ui/core": "4.6.1", + "@material-ui/icons": "4.5.1", + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "jquery": "3.3.1", + "react": "16.11.0", + "react-dom": "16.11.0", + "react-router-dom": "4.3.1" + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/mediatorApp/pom.xml b/sdnr/wt/odlux/apps/mediatorApp/pom.xml new file mode 100644 index 000000000..bc3355a39 --- /dev/null +++ b/sdnr/wt/odlux/apps/mediatorApp/pom.xml @@ -0,0 +1,148 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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"> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> + <artifactId>sdnr-wt-odlux-app-mediatorApp</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>sdnr-wt-odlux-app-mediatorApp</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-provider</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src2/main/java</sourceDirectory> + <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>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src2/test/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.1</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>v10.16.3</nodeVersion> + <yarnVersion>v1.19.0</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package>org.onap.ccsdk.features.sdnr.wt.odlux.model.*,com.opensymphony.*</Import-Package> + <Private-Package/> + </instructions> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + <resource> + <directory>src2/main/resources</directory> + </resource> + <resource> + <directory>src2/test/resources</directory> + </resource> + </resources> + </build> +</project> diff --git a/sdnr/wt/odlux/apps/mediatorApp/src/actions/avaliableMediatorServersActions.ts b/sdnr/wt/odlux/apps/mediatorApp/src/actions/avaliableMediatorServersActions.ts new file mode 100644 index 000000000..3f56b05e1 --- /dev/null +++ b/sdnr/wt/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/apps/mediatorApp/src/actions/mediatorConfigActions.ts b/sdnr/wt/odlux/apps/mediatorApp/src/actions/mediatorConfigActions.ts new file mode 100644 index 000000000..516515ab2 --- /dev/null +++ b/sdnr/wt/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/apps/mediatorApp/src/actions/mediatorServerActions.ts b/sdnr/wt/odlux/apps/mediatorApp/src/actions/mediatorServerActions.ts new file mode 100644 index 000000000..79e46df0e --- /dev/null +++ b/sdnr/wt/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/apps/mediatorApp/src/components/editMediatorConfigDialog.tsx b/sdnr/wt/odlux/apps/mediatorApp/src/components/editMediatorConfigDialog.tsx new file mode 100644 index 000000000..bcbccccd7 --- /dev/null +++ b/sdnr/wt/odlux/apps/mediatorApp/src/components/editMediatorConfigDialog.tsx @@ -0,0 +1,393 @@ +/** + * ============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, createStyles, WithStyles, withStyles, Typography, FormControlLabel, Checkbox } from '@material-ui/core'; + +import Button from '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import Select from '@material-ui/core/Select'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/DialogTitle'; + +import Tabs from '@material-ui/core/Tabs'; +import Tab from '@material-ui/core/Tab'; + +import Fab from '@material-ui/core/Fab'; +import AddIcon from '@material-ui/icons/Add'; +import DeleteIcon from '@material-ui/icons/Delete'; +import IconButton from '@material-ui/core/IconButton'; + +import { addMediatorConfigAsyncActionCreator, updateMediatorConfigAsyncActionCreator, removeMediatorConfigAsyncActionCreator } from '../actions/mediatorConfigActions'; +import { MediatorConfig, ODLConfig } from '../models/mediatorServer'; +import FormControl from '@material-ui/core/FormControl'; +import InputLabel from '@material-ui/core/InputLabel'; +import MenuItem from '@material-ui/core/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 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 fullWidth disabled={setting.readonly}> + <InputLabel htmlFor="deviceType">Device</InputLabel> + <Select 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 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 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 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 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 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 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) + ] + }); + }} ><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 className={classes.left} margin={"dense"} > + <InputLabel htmlFor={`protocol-${ind}`}>Protocoll</InputLabel> + <Select 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 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 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 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 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 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/apps/mediatorApp/src/components/editMediatorServerDialog.tsx b/sdnr/wt/odlux/apps/mediatorApp/src/components/editMediatorServerDialog.tsx new file mode 100644 index 000000000..882a6b37e --- /dev/null +++ b/sdnr/wt/odlux/apps/mediatorApp/src/components/editMediatorServerDialog.tsx @@ -0,0 +1,220 @@ +/** + * ============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 '@material-ui/core/Button'; +import TextField from '@material-ui/core/TextField'; +import Dialog from '@material-ui/core/Dialog'; +import DialogActions from '@material-ui/core/DialogActions'; +import DialogContent from '@material-ui/core/DialogContent'; +import DialogContentText from '@material-ui/core/DialogContentText'; +import DialogTitle from '@material-ui/core/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 '@material-ui/core'; + +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 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 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(); + }} > {setting.applyButtonText} </Button> + <Button onClick={(event) => { + this.onCancel(); + 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/apps/mediatorApp/src/components/showMeditaorInfoDialog.tsx b/sdnr/wt/odlux/apps/mediatorApp/src/components/showMeditaorInfoDialog.tsx new file mode 100644 index 000000000..fd8a2496a --- /dev/null +++ b/sdnr/wt/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 * as React from 'react' +import { Dialog, DialogTitle, DialogContent, DialogActions, TextField, DialogContentText, Checkbox, Button, FormControlLabel, FormGroup } from '@material-ui/core'; +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 disabled margin="dense" id="deviceIp" label="Device IP" fullWidth defaultValue={this.props.config.DeviceIp} /> + <TextField disabled margin="dense" id="deviceport" label="Device Port" fullWidth defaultValue={this.props.config.DevicePort} /> + <TextField disabled margin="dense" id="status" label="Status" fullWidth defaultValue={this.state.status} /> + <TextField disabled margin="dense" id="deviceType" label="Device Type" fullWidth defaultValue={this.state.devicetype} /> + <TextField 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 disabled margin="dense" defaultValue={element.Protocol + '://' + element.Server} label="Server" /> + <TextField disabled margin="dense" defaultValue={element.Port} label="Port" /> + <FormControlLabel control={<Checkbox disabled checked={element.Trustall} />} label="Trustall" /> + </Panel> + ) + } + + </DialogContent> + <DialogActions> + <Button onClick={this.onClose}>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/apps/mediatorApp/src/handlers/avaliableMediatorServersHandler.ts b/sdnr/wt/odlux/apps/mediatorApp/src/handlers/avaliableMediatorServersHandler.ts new file mode 100644 index 000000000..c22252d20 --- /dev/null +++ b/sdnr/wt/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/apps/mediatorApp/src/handlers/mediatorAppRootHandler.ts b/sdnr/wt/odlux/apps/mediatorApp/src/handlers/mediatorAppRootHandler.ts new file mode 100644 index 000000000..2642ec8cd --- /dev/null +++ b/sdnr/wt/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/apps/mediatorApp/src/handlers/mediatorServerHandler.ts b/sdnr/wt/odlux/apps/mediatorApp/src/handlers/mediatorServerHandler.ts new file mode 100644 index 000000000..246634cbe --- /dev/null +++ b/sdnr/wt/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/apps/mediatorApp/src/index.html b/sdnr/wt/odlux/apps/mediatorApp/src/index.html new file mode 100644 index 000000000..95bf9ec6b --- /dev/null +++ b/sdnr/wt/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/apps/mediatorApp/src/models/mediatorServer.ts b/sdnr/wt/odlux/apps/mediatorApp/src/models/mediatorServer.ts new file mode 100644 index 000000000..6ab6db8b3 --- /dev/null +++ b/sdnr/wt/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/apps/mediatorApp/src/plugin.tsx b/sdnr/wt/odlux/apps/mediatorApp/src/plugin.tsx new file mode 100644 index 000000000..5ffd0124b --- /dev/null +++ b/sdnr/wt/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 * as React from "react"; +import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; + +import { faGlobe } from '@fortawesome/free-solid-svg-icons'; // select app icon + +import applicationManager from '../../../framework/src/services/applicationManager'; +import { IApplicationStoreState } from "../../../framework/src/store/applicationStore"; +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"; + +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: faGlobe, + rootComponent: FinalApp, + rootActionHandler: mediatorAppRootHandler, + menuEntry: "Mediator" + }); + + // prefetch all avaliable mediator servers + applicationApi.applicationStoreInitialized.then(applicationStore => { + applicationStore.dispatch(avaliableMediatorServersReloadAction) + }); +}; diff --git a/sdnr/wt/odlux/apps/mediatorApp/src/services/mediatorService.ts b/sdnr/wt/odlux/apps/mediatorApp/src/services/mediatorService.ts new file mode 100644 index 000000000..aee086689 --- /dev/null +++ b/sdnr/wt/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.output.data[0]) { + const firstResult = result.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/apps/mediatorApp/src/views/mediatorApplication.tsx b/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx new file mode 100644 index 000000000..945e13507 --- /dev/null +++ b/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorApplication.tsx @@ -0,0 +1,276 @@ +/** + * ============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, createStyles, WithStyles, withStyles, Tooltip } from '@material-ui/core'; + +import AddIcon from '@material-ui/icons/Add'; +import IconButton from '@material-ui/core/IconButton'; +import EditIcon from '@material-ui/icons/Edit'; +import DeleteIcon from '@material-ui/icons/Delete'; +import InfoIcon from '@material-ui/icons/Info'; +import StartIcon from '@material-ui/icons/PlayArrow'; +import StopIcon from '@material-ui/icons/Stop'; + +import CircularProgress from '@material-ui/core/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 title={"Start"} > + <IconButton disabled={rowData[BusySymbol]} className={classes.button}> + <StartIcon onClick={(event) => { event.preventDefault(); event.stopPropagation(); this.props.startMediator(rowData.Name); }} /> + </IconButton> + </Tooltip> + <Tooltip title={"Stop"} > + <IconButton disabled={rowData[BusySymbol]} className={classes.button}> + <StopIcon onClick={(event) => { event.preventDefault(); event.stopPropagation(); this.props.stopMediator(rowData.Name); }} /> + </IconButton> + </Tooltip> + </div> + <div className={classes.spacer}> + <Tooltip title={"Info"} ><IconButton className={classes.button} onClick={(event) => { this.onOpenInfoDialog(event, rowData) }}><InfoIcon /></IconButton></Tooltip> + </div> + <div className={classes.spacer}> + {process.env.NODE_ENV === "development" ? <Tooltip title={"Edit"} ><IconButton disabled={rowData[BusySymbol]} className={classes.button} onClick={event => this.onOpenEditConfigurationDialog(event, rowData)}><EditIcon /></IconButton></Tooltip> : null} + <Tooltip title={"Remove"} ><IconButton disabled={rowData[BusySymbol]} className={classes.button} onClick={event => this.onOpenRemoveConfigutationDialog(event, rowData)}><DeleteIcon /></IconButton></Tooltip> + </div> + </> + ); + + const addMediatorConfigAction = { icon: AddIcon, tooltip: 'Add', 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 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 || ''} 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/apps/mediatorApp/src/views/mediatorServerSelection.tsx b/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx new file mode 100644 index 000000000..a5b34a2dc --- /dev/null +++ b/sdnr/wt/odlux/apps/mediatorApp/src/views/mediatorServerSelection.tsx @@ -0,0 +1,157 @@ +/** + * ============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 { WithStyles, withStyles, createStyles, Theme, Tooltip } from '@material-ui/core'; + +import AddIcon from '@material-ui/icons/Add'; +import IconButton from '@material-ui/core/IconButton'; +import EditIcon from '@material-ui/icons/Edit'; +import DeleteIcon from '@material-ui/icons/Delete'; + +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 { 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 +} + +class MediatorServerSelectionComponent extends React.Component<MediatorServerSelectionComponentProps, MediatorServerSelectionComponentState> { + + constructor(props: MediatorServerSelectionComponentProps) { + super(props); + + this.state = { + mediatorServerEditorMode: EditMediatorServerDialogMode.None, + mediatorServerToEdit: emptyMediatorServer, + } + } + + render() { + const { classes } = this.props; + + const addMediatorServerActionButton = { + icon: AddIcon, tooltip: 'Add', onClick: () => { + this.setState({ + mediatorServerEditorMode: EditMediatorServerDialogMode.AddMediatorServer, + mediatorServerToEdit: emptyMediatorServer, + }); + } + }; + return ( + <> + <MediatorServersTable title={"Mediator"} customActionButtons={[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 title={"Edit"} ><IconButton className={classes.button} onClick={event => { this.onEditMediatorServer(event, rowData); }}><EditIcon /></IconButton></Tooltip> + <Tooltip title={"Remove"} ><IconButton className={classes.button} onClick={event => { this.onRemoveMediatorServer(event, rowData); }}><DeleteIcon /></IconButton></Tooltip> + </div> + ) + } + ]} onHandleClick={this.onSelectMediatorServer} /> + <EditMediatorServerDialog + mediatorServer={this.state.mediatorServerToEdit} + mode={this.state.mediatorServerEditorMode} + onClose={this.onCloseEditMediatorServerDialog} /> + </> + ); + } + + public componentDidMount() { + this.props.mediatorServersActions.onToggleFilter(); + } + + 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, + }); + } +} + + +export const MediatorServerSelection = withStyles(styles)(connect(mapProps, mapDispatch)(MediatorServerSelectionComponent)); +export default MediatorServerSelection;
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/mediatorApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java b/sdnr/wt/odlux/apps/mediatorApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java new file mode 100644 index 000000000..1e882fc69 --- /dev/null +++ b/sdnr/wt/odlux/apps/mediatorApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles; + +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class MyOdluxBundle extends OdluxBundle { + + @Override + public void initialize() { + super.initialize(); + } + + @Override + public void clean() { + super.clean(); + } + + @Override + public String getResourceFileContent(String filename) { + return super.getResourceFileContent(filename); + } + + @Override + public boolean hasResource(String filename) { + return super.hasResource(filename); + } + + @Override + public void setBundleName(String bundleName) { + super.setBundleName(bundleName); + } + + @Override + public void setLoader(OdluxBundleLoader loader) { + super.setLoader(loader); + } + + @Override + public String getBundleName() { + return super.getBundleName(); + } + + @Override + public OdluxBundleLoader getLoader() { + return super.getLoader(); + } + + public MyOdluxBundle() { + super(); + } +} diff --git a/sdnr/wt/odlux/apps/mediatorApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml b/sdnr/wt/odlux/apps/mediatorApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..c11492bc4 --- /dev/null +++ b/sdnr/wt/odlux/apps/mediatorApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,9 @@ +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <reference id="loadersvc" availability="mandatory" activation="eager" interface="org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader"/> + + <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle"> + <property name="loader" ref="loadersvc"/> + <property name="bundleName" value="mediatorApp"/> + <property name="index" value="90"/> + </bean> +</blueprint>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/mediatorApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java b/sdnr/wt/odlux/apps/mediatorApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java new file mode 100644 index 000000000..d0fbe63c0 --- /dev/null +++ b/sdnr/wt/odlux/apps/mediatorApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.onap.ccsdk.features.sdnr.wt.odlux.OdluxBundleLoaderImpl; +import org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle; + +public class TestBundleRes { + + @Test + public void test() { + OdluxBundleLoaderImpl loader = OdluxBundleLoaderImpl.getInstance(); + MyOdluxBundle b = new MyOdluxBundle(); + b.setLoader(loader); + b.setIndex(0); + b.setBundleName("abc"); + b.initialize(); + assertTrue(loader.getNumberOfBundles()==1); + assertNotNull(b.getLoader()); + assertEquals("abc",b.getBundleName()); + assertTrue(b.hasResource("test.js")); + assertNotNull(b.getResourceFileContent("test.js")); + b.clean(); + assertTrue(loader.getNumberOfBundles()==0); + } + +} diff --git a/sdnr/wt/odlux/apps/mediatorApp/src2/test/resources/test.js b/sdnr/wt/odlux/apps/mediatorApp/src2/test/resources/test.js new file mode 100644 index 000000000..b47fdc39f --- /dev/null +++ b/sdnr/wt/odlux/apps/mediatorApp/src2/test/resources/test.js @@ -0,0 +1,5 @@ +asdac sad +as +d +sad + sadfa
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/mediatorApp/tsconfig.json b/sdnr/wt/odlux/apps/mediatorApp/tsconfig.json new file mode 100644 index 000000000..b0c9b424d --- /dev/null +++ b/sdnr/wt/odlux/apps/mediatorApp/tsconfig.json @@ -0,0 +1,38 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": false, + "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/apps/mediatorApp/tslint.json b/sdnr/wt/odlux/apps/mediatorApp/tslint.json new file mode 100644 index 000000000..b5143e92c --- /dev/null +++ b/sdnr/wt/odlux/apps/mediatorApp/tslint.json @@ -0,0 +1,4 @@ +{ + "rules":{ + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/mediatorApp/webpack.config.js b/sdnr/wt/odlux/apps/mediatorApp/webpack.config.js new file mode 100644 index 000000000..3efe2d015 --- /dev/null +++ b/sdnr/wt/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" + }] + }] + }, + + 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://10.20.6.29:28181", + secure: false + }, + "/database/": { + target: "http://10.20.6.29:28181", + secure: false + }, + "/restconf/": { + target: "http://10.20.6.29:28181", + secure: false + }, + "/help/": { + target: "http://10.20.6.29:28181", + secure: false + }, + "/ms/": { + target: "http://10.20.6.29:28181", + secure: false + }, + "/websocket": { + target: "http://10.20.6.29:28181", + ws: true, + changeOrigin: true, + secure: false + } + } + } + }]; +} diff --git a/sdnr/wt/odlux/apps/minimumApp/.babelrc b/sdnr/wt/odlux/apps/minimumApp/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt/odlux/apps/minimumApp/.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/apps/minimumApp/package.json b/sdnr/wt/odlux/apps/minimumApp/package.json new file mode 100644 index 000000000..d9b18a3ce --- /dev/null +++ b/sdnr/wt/odlux/apps/minimumApp/package.json @@ -0,0 +1,40 @@ +{ + "name": "@odlux/minimum-app", + "version": "0.1.0", + "description": "A react based modular UI to demo the minimum 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": { + "@odlux/framework": "*" + }, + "peerDependencies": { + "@types/react": "16.9.11", + "@types/react-dom": "16.9.4", + "@types/react-router-dom": "4.3.1", + "@material-ui/core": "4.6.1", + "@material-ui/icons": "4.5.1", + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "jquery": "3.3.1", + "react": "16.11.0", + "react-dom": "16.11.0", + "react-router-dom": "4.3.1" + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/minimumApp/pom.xml b/sdnr/wt/odlux/apps/minimumApp/pom.xml new file mode 100644 index 000000000..dc9507c9b --- /dev/null +++ b/sdnr/wt/odlux/apps/minimumApp/pom.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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"> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> + <artifactId>sdnr-wt-odlux-app-minimumApp</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>sdnr-wt-odlux-app-minimumApp</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-provider</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src2/main/java</sourceDirectory> + <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>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src2/test/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.1</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>v10.16.3</nodeVersion> + <yarnVersion>v1.19.0</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package>org.onap.ccsdk.features.sdnr.wt.odlux.model.*,com.opensymphony.*</Import-Package> + <Private-Package/> + </instructions> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + <resource> + <directory>src2/main/resources</directory> + </resource> + <resource> + <directory>src2/test/resources</directory> + </resource> + </resources> + </build> +</project> diff --git a/sdnr/wt/odlux/apps/minimumApp/src/handlers/minimumAppRootHandler.ts b/sdnr/wt/odlux/apps/minimumApp/src/handlers/minimumAppRootHandler.ts new file mode 100644 index 000000000..463ad21e2 --- /dev/null +++ b/sdnr/wt/odlux/apps/minimumApp/src/handlers/minimumAppRootHandler.ts @@ -0,0 +1,37 @@ +/** + * ============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'; + +export interface IMinimumAppStoreState { +} + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + minimum: IMinimumAppStoreState + } +} + +const actionHandlers = { +}; + +export const minimumAppRootHandler = combineActionHandler<IMinimumAppStoreState>(actionHandlers); +export default minimumAppRootHandler; diff --git a/sdnr/wt/odlux/apps/minimumApp/src/index.html b/sdnr/wt/odlux/apps/minimumApp/src/index.html new file mode 100644 index 000000000..58865edc1 --- /dev/null +++ b/sdnr/wt/odlux/apps/minimumApp/src/index.html @@ -0,0 +1,24 @@ +<!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>Minimal 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", "minimumApp"], function (app) { + app("./app.tsx") + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/minimumApp/src/plugin.tsx b/sdnr/wt/odlux/apps/minimumApp/src/plugin.tsx new file mode 100644 index 000000000..3095ba783 --- /dev/null +++ b/sdnr/wt/odlux/apps/minimumApp/src/plugin.tsx @@ -0,0 +1,48 @@ +/** + * ============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 * as React from "react"; +import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; + +import { faLock } from '@fortawesome/free-solid-svg-icons'; // select app icon + +import applicationManager from '../../../framework/src/services/applicationManager'; +import connect, { Connect } from '../../../framework/src/flux/connect'; + +import { minimumAppRootHandler } from './handlers/minimumAppRootHandler'; + +type AppProps = RouteComponentProps & Connect; + +const App = (props: AppProps) => ( + <div>Start your app here!!</div> +); + +const FinalApp = withRouter(connect()(App)); + +export function register() { + applicationManager.registerApplication({ + name: "minimum", + icon: faLock, + rootComponent: FinalApp, + rootActionHandler: minimumAppRootHandler, + menuEntry: "Minimum" + }); +} + + diff --git a/sdnr/wt/odlux/apps/minimumApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java b/sdnr/wt/odlux/apps/minimumApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java new file mode 100644 index 000000000..1e882fc69 --- /dev/null +++ b/sdnr/wt/odlux/apps/minimumApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles; + +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class MyOdluxBundle extends OdluxBundle { + + @Override + public void initialize() { + super.initialize(); + } + + @Override + public void clean() { + super.clean(); + } + + @Override + public String getResourceFileContent(String filename) { + return super.getResourceFileContent(filename); + } + + @Override + public boolean hasResource(String filename) { + return super.hasResource(filename); + } + + @Override + public void setBundleName(String bundleName) { + super.setBundleName(bundleName); + } + + @Override + public void setLoader(OdluxBundleLoader loader) { + super.setLoader(loader); + } + + @Override + public String getBundleName() { + return super.getBundleName(); + } + + @Override + public OdluxBundleLoader getLoader() { + return super.getLoader(); + } + + public MyOdluxBundle() { + super(); + } +} diff --git a/sdnr/wt/odlux/apps/minimumApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml b/sdnr/wt/odlux/apps/minimumApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..4c8ed1343 --- /dev/null +++ b/sdnr/wt/odlux/apps/minimumApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,9 @@ +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <reference id="loadersvc" availability="mandatory" activation="eager" interface="org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader"/> + + <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle"> + <property name="loader" ref="loadersvc"/> + <property name="bundleName" value="minimumApp"/> + <property name="index" value="999" /> + </bean> +</blueprint>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/minimumApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java b/sdnr/wt/odlux/apps/minimumApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java new file mode 100644 index 000000000..d0fbe63c0 --- /dev/null +++ b/sdnr/wt/odlux/apps/minimumApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java @@ -0,0 +1,46 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles.test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import org.junit.Test; +import org.onap.ccsdk.features.sdnr.wt.odlux.OdluxBundleLoaderImpl; +import org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle; + +public class TestBundleRes { + + @Test + public void test() { + OdluxBundleLoaderImpl loader = OdluxBundleLoaderImpl.getInstance(); + MyOdluxBundle b = new MyOdluxBundle(); + b.setLoader(loader); + b.setIndex(0); + b.setBundleName("abc"); + b.initialize(); + assertTrue(loader.getNumberOfBundles()==1); + assertNotNull(b.getLoader()); + assertEquals("abc",b.getBundleName()); + assertTrue(b.hasResource("test.js")); + assertNotNull(b.getResourceFileContent("test.js")); + b.clean(); + assertTrue(loader.getNumberOfBundles()==0); + } + +} diff --git a/sdnr/wt/odlux/apps/minimumApp/src2/test/resources/test.js b/sdnr/wt/odlux/apps/minimumApp/src2/test/resources/test.js new file mode 100644 index 000000000..b47fdc39f --- /dev/null +++ b/sdnr/wt/odlux/apps/minimumApp/src2/test/resources/test.js @@ -0,0 +1,5 @@ +asdac sad +as +d +sad + sadfa
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/minimumApp/tsconfig.json b/sdnr/wt/odlux/apps/minimumApp/tsconfig.json new file mode 100644 index 000000000..a66b5d828 --- /dev/null +++ b/sdnr/wt/odlux/apps/minimumApp/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": false, + "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": [ + "prop-types", + "react", + "react-dom" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/sdnr/wt/odlux/apps/minimumApp/webpack.config.js b/sdnr/wt/odlux/apps/minimumApp/webpack.config.js new file mode 100644 index 000000000..64a534464 --- /dev/null +++ b/sdnr/wt/odlux/apps/minimumApp/webpack.config.js @@ -0,0 +1,136 @@ +/** + * 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: { + minimumApp: ["./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" + }] + }] + }, + + 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: { + "/api": { + target: "http://localhost:3001", + secure: false + } + } + } + }]; +} diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/.babelrc b/sdnr/wt/odlux/apps/performanceHistoryApp/.babelrc new file mode 100644 index 000000000..3d8cd1260 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/.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/apps/performanceHistoryApp/package.json b/sdnr/wt/odlux/apps/performanceHistoryApp/package.json new file mode 100644 index 000000000..1e6adf7c3 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/package.json @@ -0,0 +1,43 @@ +{ + "name": "@odlux/performancehistory-app", + "version": "0.1.1", + "description": "A react based modular UI to display performance history data from a database.", + "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": "Sai Neetha Phulmali", + "license": "Apache-2.0", + "dependencies": { + "@odlux/framework": "*", + "@odlux/connect-app": "*", + "react-chartjs-2": "2.7.6", + "chart.js": "2.8.0" + }, + "peerDependencies": { + "@types/react": "16.9.11", + "@types/react-dom": "16.9.4", + "@types/react-router-dom": "4.3.1", + "@material-ui/core": "4.6.1", + "@material-ui/icons": "4.5.1", + "@types/classnames": "2.2.6", + "@types/flux": "3.1.8", + "@types/jquery": "3.3.10", + "jquery": "3.3.1", + "react": "16.11.0", + "react-dom": "16.11.0", + "react-router-dom": "4.3.1" + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml b/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml new file mode 100644 index 000000000..b6ede0c36 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml @@ -0,0 +1,147 @@ +<?xml version="1.0" encoding="UTF-8"?> +<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"> + + <parent> + <groupId>org.onap.ccsdk.parent</groupId> + <artifactId>odlparent</artifactId> + <version>1.5.1-SNAPSHOT</version> + <relativePath/> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.onap.ccsdk.features.sdnr.wt</groupId> + <artifactId>sdnr-wt-odlux-app-performanceHistoryApp</artifactId> + <version>0.7.0-SNAPSHOT</version> + <packaging>bundle</packaging> + <name>sdnr-wt-odlux-app-performanceHistoryApp</name> + <licenses> + <license> + <name>Apache License, Version 2.0</name> + <url>http://www.apache.org/licenses/LICENSE-2.0</url> + </license> + </licenses> + <dependencies> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-model</artifactId> + <version>${project.version}</version> + </dependency> + <dependency> + <groupId>${project.groupId}</groupId> + <artifactId>sdnr-wt-odlux-core-provider</artifactId> + <version>${project.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + </dependencies> + <build> + <sourceDirectory>src2/main/java</sourceDirectory> + <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>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>add-test-source</id> + <phase>generate-test-sources</phase> + <goals> + <goal>add-test-source</goal> + </goals> + <configuration> + <sources> + <source>src2/test/java</source> + </sources> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>de.jacks-it-lab</groupId> + <artifactId>frontend-maven-plugin</artifactId> + <version>1.7.1</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>v10.16.3</nodeVersion> + <yarnVersion>v1.19.0</yarnVersion> + </configuration> + </execution> + <execution> + <id>yarn build</id> + <goals> + <goal>yarn</goal> + </goals> + <configuration> + <arguments>run build</arguments> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + </plugin> + <plugin> + <groupId>org.apache.felix</groupId> + <artifactId>maven-bundle-plugin</artifactId> + <extensions>true</extensions> + <configuration> + <instructions> + <Import-Package>org.onap.ccsdk.features.sdnr.wt.odlux.model.*,com.opensymphony.*</Import-Package> + <Private-Package/> + </instructions> + </configuration> + </plugin> + </plugins> + <resources> + <resource> + <directory>dist</directory> + <targetPath>odlux</targetPath> + </resource> + <resource> + <directory>src2/main/resources</directory> + </resource> + <resource> + <directory>src2/test/resources</directory> + </resource> + </resources> + </build> +</project> diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/deviceListActions.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/deviceListActions.ts new file mode 100644 index 000000000..9637fec81 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/deviceListActions.ts @@ -0,0 +1,78 @@ +/** + * ============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 { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import { DeviceListType } from '../models/deviceListType'; +import { PerformanceHistoryService } from '../services/performanceHistoryService'; + +/** + * Represents the base action. + */ +export class BaseAction extends Action { } + +/** + * Represents an action causing the store to load all devices. + */ +export class LoadAllDeviceListAction extends BaseAction { } + +/** + * Represents an action causing the store to update all devices. + */ +export class AllDeviceListLoadedAction extends BaseAction { + /** + * Initialize this instance. + * + * @param deviceList All the distinct devices from the performance history database. + */ + constructor(public deviceList: DeviceListType[] | null, public error?: string) { + super(); + } +} + +/** + * Represents an asynchronous thunk action to load all devices. + */ +export const loadAllDeviceListAsync = async (dispatch: Dispatch) => { + dispatch(new LoadAllDeviceListAction()); + const deviceListFromPerfHistory: DeviceListType[] = await PerformanceHistoryService.getDeviceListfromPerf15minHistory().then(ne => (ne)) || []; + const deviceListFromPerf24History: DeviceListType[] = await PerformanceHistoryService.getDeviceListfromPerf24hHistory().then(ne => (ne)) || []; + deviceListFromPerf24History.forEach(deviceList24h => { + if (deviceListFromPerfHistory.findIndex(deviceList15min => deviceList15min.nodeId === deviceList24h.nodeId) < 0) { + deviceListFromPerfHistory.push(deviceList24h); + }; + }); + return deviceListFromPerfHistory && dispatch(new AllDeviceListLoadedAction(deviceListFromPerfHistory)); +}; + +/** + * Represents an action causing the store to update mountId. + */ +export class UpdateMountId extends BaseAction { + constructor(public nodeId?: string) { + super(); + } +} + +/** + * Represents an asynchronous thunk action to load updated mountId. + */ +export const updateMountIdActionCreator = (nodeId: string) => async (dispatch: Dispatch) => { + return dispatch(new UpdateMountId(nodeId)); +} diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts new file mode 100644 index 000000000..2741d889a --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/ltpAction.ts @@ -0,0 +1,78 @@ +/** + * ============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 { LtpIds } from '../models/availableLtps'; +import { PerformanceHistoryService } from '../services/performanceHistoryService'; + +/** + * Represents the base action. + */ +export class BaseAction extends Action { } + +/** + * Represents an action causing the store to load available ltps. + */ +export class LoadAllAvailableLtpsAction extends BaseAction { } + +/** + * Represents an action causing the store to update available ltps. + */ +export class AllAvailableLtpsLoadedAction extends BaseAction { + /** + * Initialize this instance. + * @param availableLtps The available ltps which are returned from the database. + */ + constructor(public availableLtps: LtpIds[] | null, public error?: string) { + super(); + } +} + + +/** + * Represents an asynchronous thunk action to load available distinctLtps by networkElement from the database and set the returned first Ltp as default. + * @param networkElement The network element sent to database to get its available distinct Ltps. + * @param selectedTimePeriod The time period selected sent to database to get the distinct Ltps of the selected network element. + * @param selectedLtp The Ltp which is selected in the dropdown. + * @param selectFirstLtp The function to get the first ltp returned from the database to be selected as default on selection upon network element. + * @param resetLtp The function to verify if the selected ltp is also available in the selected time period database else reset the Ltp dropdown to select. + */ +export const loadDistinctLtpsbyNetworkElementAsync = (networkElement: string, selectedTimePeriod: string, selectedLtp: string, selectFirstLtp?: Function, resetLtp?: Function) => (dispatch: Dispatch) => { + dispatch(new LoadAllAvailableLtpsAction()); + PerformanceHistoryService.getDistinctLtpsFromDatabase(networkElement, selectedTimePeriod).then(distinctLtps => { + if (distinctLtps) { + const ltps = getDistinctLtps(distinctLtps, selectedLtp, selectFirstLtp, resetLtp); + dispatch(new AllAvailableLtpsLoadedAction(ltps)); + } + }).catch(error => { + dispatch(new AllAvailableLtpsLoadedAction(null, error)); + }); +}; + +const getDistinctLtps = (distinctLtps: LtpIds[], selectedLtp: string, selectFirstLtp?: Function, resetLtp?: Function) => { + let ltpNotSelected: boolean = true; + selectFirstLtp && selectFirstLtp(distinctLtps[0].key); + distinctLtps.forEach((value: LtpIds) => { + if (value.key === selectedLtp) { + ltpNotSelected = false; + } + }); + resetLtp && resetLtp(ltpNotSelected); + return distinctLtps; +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/panelChangeActions.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/panelChangeActions.ts new file mode 100644 index 000000000..8b77cb3b0 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/panelChangeActions.ts @@ -0,0 +1,32 @@ +/** + * ============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 { PanelId } from '../models/panelId'; + +/** + * Represents an action causing the store to update the panel. + */ +export class SetPanelAction extends Action { + /** + * Initialize this instance. + * @param panelId Action to set the current panel by its Id. + */ + constructor(public panelId: PanelId) { + super(); + } +} diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/timeChangeAction.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/timeChangeAction.ts new file mode 100644 index 000000000..a069af13c --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/actions/timeChangeAction.ts @@ -0,0 +1,29 @@ +/** + * ============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 { PmDataInterval } from '../models/performanceDataType'; + +export class TimeChangeAction extends Action { + /** + * Initialize this instance. + * @param time Action to set the time interval in dropdown. + */ + constructor(public time: PmDataInterval) { + super(); + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx new file mode 100644 index 000000000..09d3accd4 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/adaptiveModulation.tsx @@ -0,0 +1,469 @@ +/** + * ============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 { withRouter, RouteComponentProps } from 'react-router-dom'; + +import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + +import { AdaptiveModulationDataType, AdaptiveModulationDatabaseDataType } from '../models/adaptiveModulationDataType'; +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; +import { createAdaptiveModulationProperties, createAdaptiveModulationActions } from '../handlers/adaptiveModulationHandler'; +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; + +const mapProps = (state: IApplicationStoreState) => ({ + adaptiveModulationProperties: createAdaptiveModulationProperties(state), +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + adaptiveModulationActions: createAdaptiveModulationActions(dispatcher.dispatch), +}); + +type AdaptiveModulationComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { + selectedTimePeriod: string +}; + +const AdaptiveModulationTable = MaterialTable as MaterialTableCtorType<AdaptiveModulationDataType>; + +/** + * The Component which gets the adaptiveModulation data from the database based on the selected time period. + */ +class AdaptiveModulationComponent extends React.Component<AdaptiveModulationComponentProps>{ + render(): JSX.Element { + const properties = this.props.adaptiveModulationProperties; + const actions = this.props.adaptiveModulationActions; + + const chartPagedData = this.getChartDataValues(properties.rows); + const adaptiveModulationColumns: ColumnModel<AdaptiveModulationDataType>[] = [ + { property: "radioSignalId", title: "Radio signal", type: ColumnType.text }, + { property: "scannerId", title: "Scanner ID", type: ColumnType.text }, + { property: "timeStamp", title: "End Time", type: ColumnType.text }, + { + property: "suspectIntervalFlag", title: "Suspect Interval", customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspectIntervalFlag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + }]; + + chartPagedData.datasets.forEach(ds => { + adaptiveModulationColumns.push(addColumnLabels<AdaptiveModulationDataType>(ds.name, ds.columnLabel)); + }); + + return ( + <> + {lineChart(chartPagedData)} + <AdaptiveModulationTable idProperty={"_id"} columns={adaptiveModulationColumns} {...properties} {...actions} /> + </> + ); + }; + + /** + * This function gets the performance values for Adaptive modulation according on the chartjs dataset structure + * which is to be sent to the chart. + */ + + private getChartDataValues = (rows: AdaptiveModulationDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "time2StatesS", + label: "QAM2S", + borderColor: '#62a309fc', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM2S", + }, { + name: "time2States", + label: "QAM2", + borderColor: '#62a309fc', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM2", + }, { + name: "time2StatesL", + label: "QAM2L", + borderColor: '#62a309fc', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM2L", + }, { + name: "time4StatesS", + label: "QAM4S", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM4S", + }, { + name: "time4States", + label: "QAM4", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM4", + }, { + name: "time4StatesL", + label: "QAM4L", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM4L", + }, { + name: "time16StatesS", + label: "QAM16S", + borderColor: '#9b15e2', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM16S", + }, { + name: "time16States", + label: "QAM16", + borderColor: '#9b15e2', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM16", + }, { + name: "time16StatesL", + label: "QAM16L", + borderColor: '#9b15e2', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM16L", + }, { + name: "time32StatesS", + label: "QAM32S", + borderColor: '#2704f5f0', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM32S", + }, { + name: "time32States", + label: "QAM32", + borderColor: '#2704f5f0', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM32", + }, { + name: "time32StatesL", + label: "QAM32L", + borderColor: '#2704f5f0', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM32L", + }, { + name: "time64StatesS", + label: "QAM64S", + borderColor: '#347692', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM64S", + }, { + name: "time64States", + label: "QAM64", + borderColor: '#347692', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM64", + }, { + name: "time64StatesL", + label: "QAM64L", + borderColor: '#347692', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM64L", + }, { + name: "time128StatesS", + label: "QAM128S", + borderColor: '#885e22', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM128S", + }, { + name: "time128States", + label: "QAM128", + borderColor: '#885e22', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM128", + }, { + name: "time128StatesL", + label: "QAM128L", + borderColor: '#885e22', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM128L", + }, { + name: "time256StatesS", + label: "QAM256S", + borderColor: '#de07807a', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM256S", + }, { + name: "time256States", + label: "QAM256", + borderColor: '#de07807a', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM256", + }, { + name: "time256StatesL", + label: "QAM256L", + borderColor: '#de07807a', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM256L", + }, { + name: "time512StatesS", + label: "QAM512S", + borderColor: '#8fdaacde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM512S", + }, { + name: "time512States", + label: "QAM512", + borderColor: '#8fdaacde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM512", + }, { + + name: "time512StatesL", + label: "QAM512L", + borderColor: '#8fdaacde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM512L", + }, { + + name: "time1024StatesS", + label: "QAM1024S", + borderColor: '#435b22', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM1024S", + }, { + + name: "time1024States", + label: "QAM1024", + borderColor: '#435b22', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM1024", + }, { + + name: "time1024StatesL", + label: "QAM1024L", + borderColor: '#435b22', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM1024L", + }, { + name: "time2048StatesS", + label: "QAM2048S", + borderColor: '#e87a5b', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM2048S", + }, { + name: "time2048States", + label: "QAM2048", + borderColor: '#e87a5b', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM2048", + }, { + name: "time2048StatesL", + label: "QAM2048L", + borderColor: '#e87a5b', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM2048L", + }, { + name: "time4096StatesS", + label: "QAM4096S", + borderColor: '#5be878', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM4096S", + }, { + name: "time4096States", + label: "QAM4096", + borderColor: '#5be878', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM4096", + }, { + name: "time4096StatesL", + label: "QAM4096L", + borderColor: '#5be878', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM4096L", + }, { + name: "time8192StatesS", + label: "QAM8192s", + borderColor: '#cb5be8', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM8192S", + }, { + name: "time8192States", + label: "QAM8192", + borderColor: '#cb5be8', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM8192", + }, { + name: "time8192StatesL", + label: "QAM8192L", + borderColor: '#cb5be8', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "QAM8192L", + } + ]; + + _rows.forEach(row => { + row.time2StatesS = row.performanceData.time2StatesS + row.time2States = row.performanceData.time2States; + row.time2StatesL = row.performanceData.time2StatesL; + row.time4StatesS = row.performanceData.time4StatesS + row.time4States = row.performanceData.time4States; + row.time4StatesL = row.performanceData.time4StatesL; + row.time16StatesS = row.performanceData.time16StatesS; + row.time16States = row.performanceData.time16States; + row.time16StatesL = row.performanceData.time16StatesL; + row.time32StatesS = row.performanceData.time32StatesS; + row.time32States = row.performanceData.time32States; + row.time32StatesL = row.performanceData.time32StatesL; + row.time64StatesS = row.performanceData.time64StatesS; + row.time64States = row.performanceData.time64States; + row.time64StatesL = row.performanceData.time64StatesL; + row.time128StatesS = row.performanceData.time128StatesS; + row.time128States = row.performanceData.time128States; + row.time128StatesL = row.performanceData.time128StatesL; + row.time256StatesS = row.performanceData.time256StatesS; + row.time256States = row.performanceData.time256States; + row.time256StatesL = row.performanceData.time256StatesL; + row.time512StatesS = row.performanceData.time512StatesS; + row.time512States = row.performanceData.time512States; + row.time512StatesL = row.performanceData.time512StatesL; + row.time1024StatesS = row.performanceData.time1024StatesS; + row.time1024States = row.performanceData.time1024States; + row.time1024StatesL = row.performanceData.time1024StatesL; + row.time2048StatesS = row.performanceData.time2048StatesS; + row.time2048States = row.performanceData.time2048States; + row.time2048StatesL = row.performanceData.time2048StatesL; + row.time4096StatesS = row.performanceData.time4096StatesS; + row.time4096States = row.performanceData.time4096States; + row.time4096StatesL = row.performanceData.time4096StatesL; + row.time8192StatesS = row.performanceData.time8192StatesS; + row.time8192States = row.performanceData.time8192States; + row.time8192StatesL = row.performanceData.time8192StatesL; + datasets.forEach(ds => { + ds.data.push({ + x: row["timeStamp" as keyof AdaptiveModulationDataType] as string, + y: row.performanceData[ds.name as keyof AdaptiveModulationDatabaseDataType] as string + }); + }); + }); + + return { + datasets: datasets + }; + } +} +const AdaptiveModulation = withRouter(connect(mapProps, mapDisp)(AdaptiveModulationComponent)); +export default AdaptiveModulation; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx new file mode 100644 index 000000000..267e00bbe --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx @@ -0,0 +1,133 @@ +/** + * ============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 { withRouter, RouteComponentProps } from 'react-router-dom'; + +import { MaterialTable, ColumnType, MaterialTableCtorType, ColumnModel } from '../../../../framework/src/components/material-table'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + +import { CrossPolarDiscriminationDataType, CrossPolarDiscriminationDatabaseDataType } from '../models/crossPolarDiscriminationDataType'; +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; +import { createCrossPolarDiscriminationProperties, createCrossPolarDiscriminationActions } from '../handlers/crossPolarDiscriminationHandler'; +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; + +const mapProps = (state: IApplicationStoreState) => ({ + crossPolarDiscriminationProperties: createCrossPolarDiscriminationProperties(state), +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + crossPolarDiscriminationActions: createCrossPolarDiscriminationActions(dispatcher.dispatch), +}); + +type CrossPolarDiscriminationComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { + selectedTimePeriod: string +}; + +const CrossPolarDiscriminationTable = MaterialTable as MaterialTableCtorType<CrossPolarDiscriminationDataType>; + +/** + * The Component which gets the crossPolarDiscrimination data from the database based on the selected time period. + */ +class CrossPolarDiscriminationComponent extends React.Component<CrossPolarDiscriminationComponentProps>{ + render(): JSX.Element { + const properties = this.props.crossPolarDiscriminationProperties; + const actions = this.props.crossPolarDiscriminationActions; + + const chartPagedData = this.getChartDataValues(properties.rows); + + const cpdColumns: ColumnModel<CrossPolarDiscriminationDataType>[] = [ + { property: "radioSignalId", title: "Radio signal", type: ColumnType.text }, + { property: "scannerId", title: "Scanner ID", type: ColumnType.text }, + { property: "timeStamp", title: "End Time", type: ColumnType.text }, + { + property: "suspectIntervalFlag", title: "Suspect Interval", customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspectIntervalFlag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + } + ]; + + chartPagedData.datasets.forEach(ds => { + cpdColumns.push(addColumnLabels<CrossPolarDiscriminationDataType>(ds.name, ds.columnLabel)); + }); + return ( + <> + {lineChart(chartPagedData)} + <CrossPolarDiscriminationTable idProperty={"_id"} columns={cpdColumns} {...properties} {...actions} /> + </> + ); + }; + + /** + * This function gets the performance values for CPD according on the chartjs dataset structure + * which is to be sent to the chart. + */ + private getChartDataValues = (rows: CrossPolarDiscriminationDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "xpdMin", + label: "xpd-min", + borderColor: '#0e17f3de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "CPD (min)[db]" + }, { + name: "xpdAvg", + label: "xpd-avg", + borderColor: '#08edb6de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "CPD (avg)[db]" + }, { + name: "xpdMax", + label: "xpd-max", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "CPD (max)[db]" + }]; + + _rows.forEach(row => { + row.xpdMin = row.performanceData.xpdMin; + row.xpdAvg = row.performanceData.xpdAvg; + row.xpdMax = row.performanceData.xpdMax; + datasets.forEach(ds => { + ds.data.push({ + x: row["timeStamp" as keyof CrossPolarDiscriminationDataType] as string, + y: row.performanceData[ds.name as keyof CrossPolarDiscriminationDatabaseDataType] as string + }); + }); + }); + return { + datasets: datasets + }; + } +} +const CrossPolarDiscrimination = withRouter(connect(mapProps, mapDisp)(CrossPolarDiscriminationComponent)); +export default CrossPolarDiscrimination; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx new file mode 100644 index 000000000..c58c49c9e --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx @@ -0,0 +1,133 @@ +/** + * ============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 { withRouter, RouteComponentProps } from 'react-router-dom'; + +import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { PerformanceDataType, PerformanceDatabaseDataType } from '../models/performanceDataType'; +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; +import { createPerformanceDataProperties, createPerformanceDataActions } from '../handlers/performanceDataHandler'; +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; + +const mapProps = (state: IApplicationStoreState) => ({ + performanceDataProperties: createPerformanceDataProperties(state), +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + performanceDataActions: createPerformanceDataActions(dispatcher.dispatch), +}); + +type PerformanceDataComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { + selectedTimePeriod: string +}; + +const PerformanceDataTable = MaterialTable as MaterialTableCtorType<PerformanceDataType>; + +/** + * The Component which gets the performance data from the database based on the selected time period. + */ +class PerformanceDataComponent extends React.Component<PerformanceDataComponentProps>{ + render(): JSX.Element { + const properties = this.props.performanceDataProperties; + const actions = this.props.performanceDataActions; + + const chartPagedData = this.getChartDataValues(properties.rows); + const performanceColumns: ColumnModel<PerformanceDataType>[] = [ + { property: "radioSignalId", title: "Radio signal", type: ColumnType.text }, + { property: "scannerId", title: "Scanner ID", type: ColumnType.text }, + { property: "timeStamp", title: "End Time", type: ColumnType.text }, + { + property: "suspectIntervalFlag", title: "Suspect Interval", customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspectIntervalFlag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + } + ]; + + chartPagedData.datasets.forEach(ds => { + performanceColumns.push(addColumnLabels<PerformanceDataType>(ds.name, ds.columnLabel)); + }); + return ( + <> + {lineChart(chartPagedData)} + <PerformanceDataTable idProperty={"_id"} columns={performanceColumns} {...properties} {...actions} /> + </> + ); + }; + + /** + * This function gets the performance values for PerformanceData according on the chartjs dataset structure + * which is to be sent to the chart. + */ + private getChartDataValues = (rows: PerformanceDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "es", + label: "es", + borderColor: '#0e17f3de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "ES" + }, { + name: "ses", + label: "ses", + borderColor: '#08edb6de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "SES" + }, { + name: "unavailability", + label: "unavailability", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Unavailability" + }]; + + _rows.forEach(row => { + row.es = row.performanceData.es; + row.ses = row.performanceData.ses; + row.unavailability = row.performanceData.unavailability; + datasets.forEach(ds => { + ds.data.push({ + x: row["timeStamp" as keyof PerformanceDataType] as string, + y: row.performanceData[ds.name as keyof PerformanceDatabaseDataType] as string + }); + }); + }); + + return { + datasets: datasets + }; + } +} + +const PerformanceData = withRouter(connect(mapProps, mapDisp)(PerformanceDataComponent)); +export default PerformanceData; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx new file mode 100644 index 000000000..55bc39860 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx @@ -0,0 +1,134 @@ +/** + * ============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 { withRouter, RouteComponentProps } from 'react-router-dom'; + +import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + +import { ReceiveLevelDataType,ReceiveLevelDatabaseDataType } from '../models/receiveLevelDataType'; +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; +import { createReceiveLevelProperties, createReceiveLevelActions } from '../handlers/receiveLevelHandler'; +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; + +const mapProps = (state: IApplicationStoreState) => ({ + receiveLevelProperties: createReceiveLevelProperties(state), +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + receiveLevelActions: createReceiveLevelActions(dispatcher.dispatch), +}); + +type ReceiveLevelComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { + selectedTimePeriod: string +}; + +const ReceiveLevelTable = MaterialTable as MaterialTableCtorType<ReceiveLevelDataType>; + +/** + * The Component which gets the receiveLevel data from the database based on the selected time period. + */ +class ReceiveLevelComponent extends React.Component<ReceiveLevelComponentProps>{ + render(): JSX.Element { + const properties = this.props.receiveLevelProperties; + const actions = this.props.receiveLevelActions; + + const chartPagedData = this.getChartDataValues(properties.rows); + const receiveLevelColumns: ColumnModel<ReceiveLevelDataType>[] = [ + { property: "radioSignalId", title: "Radio signal", type: ColumnType.text }, + { property: "scannerId", title: "Scanner ID", type: ColumnType.text }, + { property: "timeStamp", title: "End Time", type: ColumnType.text }, + { + property: "suspectIntervalFlag", title: "Suspect Interval", customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspectIntervalFlag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + } + ]; + + chartPagedData.datasets.forEach(ds => { + receiveLevelColumns.push(addColumnLabels<ReceiveLevelDataType>(ds.name, ds.columnLabel)); + }); + + return ( + <> + {lineChart(chartPagedData)} + <ReceiveLevelTable idProperty={"_id"} columns={receiveLevelColumns} {...properties} {...actions} /> + </> + ); + }; + + /** + * This function gets the performance values for ReceiveLevel according on the chartjs dataset structure + * which is to be sent to the chart. + */ + private getChartDataValues = (rows: ReceiveLevelDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "rxLevelMin", + label: "rx-level-min", + borderColor: '#0e17f3de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Rx min" + }, { + name: "rxLevelAvg", + label: "rx-level-avg", + borderColor: '#08edb6de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Rx avg" + }, { + name: "rxLevelMax", + label: "rx-level-max", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Rx max" + }]; + + _rows.forEach(row => { + row.rxLevelMin = row.performanceData.rxLevelMin; + row.rxLevelAvg = row.performanceData.rxLevelAvg; + row.rxLevelMax = row.performanceData.rxLevelMax; + datasets.forEach(ds => { + ds.data.push({ + x: row["timeStamp" as keyof ReceiveLevelDataType] as string, + y: row.performanceData[ds.name as keyof ReceiveLevelDatabaseDataType] as string + }); + }); + }); + return { + datasets: datasets + }; + } +} + +const ReceiveLevel = withRouter(connect(mapProps, mapDisp)(ReceiveLevelComponent)); +export default ReceiveLevel; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx new file mode 100644 index 000000000..42aa007a9 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx @@ -0,0 +1,136 @@ +/** + * ============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 { withRouter, RouteComponentProps } from 'react-router-dom'; + +import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + +import { SignalToInterferenceDataType, SignalToInterferenceDatabaseDataType } from '../models/signalToInteferenceDataType'; +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; +import { createSignalToInterferenceProperties, createSignalToInterferenceActions } from '../handlers/signalToInterferenceHandler'; +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; + +const mapProps = (state: IApplicationStoreState) => ({ + signalToInterferenceProperties: createSignalToInterferenceProperties(state), +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + signalToInterferenceActions: createSignalToInterferenceActions(dispatcher.dispatch), +}); + +type SignalToInterferenceComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { + selectedTimePeriod: string +}; + +const SignalToInterferenceTable = MaterialTable as MaterialTableCtorType<SignalToInterferenceDataType>; + +/** + * The Component which gets the signal to interference data from the database based on the selected time period. + */ +class SignalToInterferenceComponent extends React.Component<SignalToInterferenceComponentProps>{ + render(): JSX.Element { + const properties = this.props.signalToInterferenceProperties; + const actions = this.props.signalToInterferenceActions; + + const chartPagedData = this.getChartDataValues(properties.rows); + + const sinrColumns: ColumnModel<SignalToInterferenceDataType>[] = [ + { property: "radioSignalId", title: "Radio signal", type: ColumnType.text }, + { property: "scannerId", title: "Scanner ID", type: ColumnType.text }, + { property: "timeStamp", title: "End Time", type: ColumnType.text }, + { + property: "suspectIntervalFlag", title: "Suspect Interval", customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspectIntervalFlag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + } + ]; + + chartPagedData.datasets.forEach(ds => { + sinrColumns.push(addColumnLabels<SignalToInterferenceDataType>(ds.name, ds.columnLabel)); + }); + return ( + <> + {lineChart(chartPagedData)} + <SignalToInterferenceTable idProperty={"_id"} columns={sinrColumns} {...properties} {...actions} + /> + </> + ); + }; + + /** + * This function gets the performance values for SINR according on the chartjs dataset structure + * which is to be sent to the chart. + */ + + private getChartDataValues = (rows: SignalToInterferenceDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "snirMin", + label: "snir-min", + borderColor: '#0e17f3de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "SINR (min)[db]" + }, { + name: "snirAvg", + label: "snir-avg", + borderColor: '#08edb6de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "SINR (avg)[db]" + }, { + name: "snirMax", + label: "snir-max", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "SINR (max)[db]" + }]; + + _rows.forEach(row => { + row.snirMin = row.performanceData.snirMin; + row.snirAvg = row.performanceData.snirAvg; + row.snirMax = row.performanceData.snirMax; + datasets.forEach(ds => { + ds.data.push({ + x: row["timeStamp" as keyof SignalToInterferenceDataType] as string, + y: row.performanceData[ds.name as keyof SignalToInterferenceDatabaseDataType] as string + }); + }); + }); + return { + datasets: datasets + }; + } +} + +const SignalToInterference = withRouter(connect(mapProps, mapDisp)(SignalToInterferenceComponent)); +export default SignalToInterference; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx new file mode 100644 index 000000000..256911c2d --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx @@ -0,0 +1,134 @@ +/** + * ============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 { withRouter, RouteComponentProps } from 'react-router-dom'; + +import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + +import { TemperatureDataType, TemperatureDatabaseDataType } from '../models/temperatureDataType'; +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; +import { createTemperatureProperties, createTemperatureActions } from '../handlers/temperatureHandler'; +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; + +const mapProps = (state: IApplicationStoreState) => ({ + temperatureProperties: createTemperatureProperties(state), +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + temperatureActions: createTemperatureActions(dispatcher.dispatch), +}); + +type TemperatureComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { + selectedTimePeriod: string +}; + +const TemperatureTable = MaterialTable as MaterialTableCtorType<TemperatureDataType>; + +/** + * The Component which gets the temperature data from the database based on the selected time period. + */ +class TemperatureComponent extends React.Component<TemperatureComponentProps>{ + render(): JSX.Element { + const properties = this.props.temperatureProperties; + const actions = this.props.temperatureActions; + + const chartPagedData = this.getChartDataValues(properties.rows); + const temperatureColumns: ColumnModel<TemperatureDataType>[] = [ + { property: "radioSignalId", title: "Radio signal", type: ColumnType.text }, + { property: "scannerId", title: "Scanner ID", type: ColumnType.text }, + { property: "timeStamp", title: "End Time", type: ColumnType.text }, + { + property: "suspectIntervalFlag", title: "Suspect Interval", customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspectIntervalFlag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + } + ]; + + chartPagedData.datasets.forEach(ds => { + temperatureColumns.push(addColumnLabels<TemperatureDataType>(ds.name, ds.columnLabel)); + }); + return ( + <> + {lineChart(chartPagedData)} + <TemperatureTable idProperty={"_id"} columns={temperatureColumns} {...properties} {...actions} /> + </> + ); + }; + + /** + * This function gets the performance values for Temperature according on the chartjs dataset structure + * which is to be sent to the chart. + */ + + private getChartDataValues = (rows: TemperatureDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "rfTempMin", + label: "rf-temp-min", + borderColor: '#0e17f3de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Rf Temp Min[deg C]" + }, { + name: "rfTempAvg", + label: "rf-temp-avg", + borderColor: '#08edb6de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Rf Temp Avg[deg C]" + }, { + name: "rfTempMax", + label: "rf-temp-max", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Rf Temp Max[deg C]" + }]; + + _rows.forEach(row => { + row.rfTempMin = row.performanceData.rfTempMin; + row.rfTempAvg = row.performanceData.rfTempAvg; + row.rfTempMax = row.performanceData.rfTempMax; + datasets.forEach(ds => { + ds.data.push({ + x: row["timeStamp" as keyof TemperatureDataType] as string, + y: row.performanceData[ds.name as keyof TemperatureDatabaseDataType] as string + }); + }); + }); + return { + datasets: datasets + }; + } +} + +const Temperature = withRouter(connect(mapProps, mapDisp)(TemperatureComponent)); +export default Temperature; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx new file mode 100644 index 000000000..635cbf103 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx @@ -0,0 +1,136 @@ +/** + * ============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 { withRouter, RouteComponentProps } from 'react-router-dom'; + +import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + +import { TransmissionPowerDataType, TransmissionPowerDatabaseDataType } from '../models/transmissionPowerDataType'; +import { IDataSet, IDataSetsObject } from '../models/chartTypes'; +import { createTransmissionPowerProperties, createTransmissionPowerActions } from '../handlers/transmissionPowerHandler'; +import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; +import { addColumnLabels } from '../utils/tableUtils'; + +const mapProps = (state: IApplicationStoreState) => ({ + transmissionPowerProperties: createTransmissionPowerProperties(state), +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + transmissionPowerActions: createTransmissionPowerActions(dispatcher.dispatch), +}); + +type TransmissionPowerComponentProps = RouteComponentProps & Connect<typeof mapProps, typeof mapDisp> & { + selectedTimePeriod: string +} + +const TransmissionPowerTable = MaterialTable as MaterialTableCtorType<TransmissionPowerDataType>; + +/** + * The Component which gets the transmission power data from the database based on the selected time period. + */ +class TransmissionPowerComponent extends React.Component<TransmissionPowerComponentProps>{ + render(): JSX.Element { + const properties = this.props.transmissionPowerProperties + const actions = this.props.transmissionPowerActions + + const chartPagedData = this.getChartDataValues(properties.rows); + + const transmissionColumns: ColumnModel<TransmissionPowerDataType>[] = [ + { property: "radioSignalId", title: "Radio signal", type: ColumnType.text }, + { property: "scannerId", title: "Scanner ID", type: ColumnType.text }, + { property: "timeStamp", title: "End Time", type: ColumnType.text }, + { + property: "suspectIntervalFlag", title: "Suspect Interval", customControl: ({ rowData }) => { + const suspectIntervalFlag = rowData["suspectIntervalFlag"].toString(); + return <div >{suspectIntervalFlag} </div> + } + } + ]; + + chartPagedData.datasets.forEach(ds => { + transmissionColumns.push(addColumnLabels<TransmissionPowerDataType>(ds.name, ds.columnLabel)); + }); + + return ( + <> + {lineChart(chartPagedData)} + <TransmissionPowerTable idProperty={"_id"} columns={transmissionColumns} {...properties} {...actions} /> + </> + ); + }; + + /** + * This function gets the performance values for TransmissionPower according on the chartjs dataset structure + * which is to be sent to the chart. + */ + + private getChartDataValues = (rows: TransmissionPowerDataType[]): IDataSetsObject => { + const _rows = [...rows]; + sortDataByTimeStamp(_rows); + + const datasets: IDataSet[] = [{ + name: "txLevelMin", + label: "tx-level-min", + borderColor: '#0e17f3de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Tx min" + }, { + name: "txLevelAvg", + label: "tx-level-avg", + borderColor: '#08edb6de', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Tx avg" + }, { + name: "txLevelMax", + label: "tx-level-max", + borderColor: '#b308edde', + bezierCurve: false, + lineTension: 0, + fill: false, + data: [], + columnLabel: "Tx max" + }]; + + _rows.forEach(row => { + row.txLevelMin = row.performanceData.txLevelMin; + row.txLevelAvg = row.performanceData.txLevelAvg; + row.txLevelMax = row.performanceData.txLevelMax; + datasets.forEach(ds => { + ds.data.push({ + x: row["timeStamp" as keyof TransmissionPowerDataType] as string, + y: row.performanceData[ds.name as keyof TransmissionPowerDatabaseDataType] as string + }); + }); + }); + return { + datasets: datasets + }; + } +} + +const TransmissionPower = withRouter(connect(mapProps, mapDisp)(TransmissionPowerComponent)); +export default TransmissionPower; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/adaptiveModulationHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/adaptiveModulationHandler.ts new file mode 100644 index 000000000..3548bf4dc --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/adaptiveModulationHandler.ts @@ -0,0 +1,37 @@ +/** + * ============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 { AdaptiveModulationDataType } from '../models/adaptiveModulationDataType'; +import { getFilter } from '../utils/tableUtils'; + +export interface IAdaptiveModulationState extends IExternalTableState<AdaptiveModulationDataType> { } + +/** + * Creates elastic search material data fetch handler for Adaptive modulation from historicalperformance database. + */ +const adaptiveModulationSearchHandler = createSearchDataHandler<AdaptiveModulationDataType>(getFilter, null) +export const { + actionHandler: adaptiveModulationActionHandler, + createActions: createAdaptiveModulationActions, + createProperties: createAdaptiveModulationProperties, + createPreActions: createAdaptiveModulationPreActions, + reloadAction: adaptiveModulationReloadAction, +} = createExternal<AdaptiveModulationDataType>(adaptiveModulationSearchHandler, appState => appState.performanceHistory.adaptiveModulation); + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts new file mode 100644 index 000000000..b3b284b42 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts @@ -0,0 +1,60 @@ +/** + * ============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 { IActionHandler } from '../../../../framework/src/flux/action'; + +import { + AllAvailableLtpsLoadedAction, + LoadAllAvailableLtpsAction, +} from '../actions/ltpAction'; + +import { LtpIds } from '../models/availableLtps'; + +export interface IAvailableLtpsState { + distinctLtps: LtpIds[]; + busy: boolean; +} + +const ltpListStateInit: IAvailableLtpsState = { + distinctLtps: [], + busy: false +}; + +export const availableLtpsActionHandler: IActionHandler<IAvailableLtpsState> = (state = ltpListStateInit, action) => { + if (action instanceof LoadAllAvailableLtpsAction) { + + state = { + ...state, + busy: true + }; + + } else if (action instanceof AllAvailableLtpsLoadedAction) { + if (!action.error && action.availableLtps) { + state = { + ...state, + distinctLtps: action.availableLtps, + busy: false + }; + } else { + state = { + ...state, + busy: false + }; + } + } + return state; +};
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/crossPolarDiscriminationHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/crossPolarDiscriminationHandler.ts new file mode 100644 index 000000000..1e6c6d0a6 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/crossPolarDiscriminationHandler.ts @@ -0,0 +1,38 @@ +/** + * ============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 { CrossPolarDiscriminationDataType } from '../models/crossPolarDiscriminationDataType'; +import { getFilter } from '../utils/tableUtils'; + +export interface ICrossPolarDiscriminationState extends IExternalTableState<CrossPolarDiscriminationDataType> { } + +/** + * Creates elastic search material data fetch handler for CPD from historicalperformance database. + */ +const crossPolarDiscriminationSearchHandler = createSearchDataHandler<CrossPolarDiscriminationDataType>(getFilter, null) + +export const { + actionHandler: crossPolarDiscriminationActionHandler, + createActions: createCrossPolarDiscriminationActions, + createProperties: createCrossPolarDiscriminationProperties, + createPreActions: createCrossPolarDiscriminationPreActions, + reloadAction: crossPolarDiscriminationReloadAction, +} = createExternal<CrossPolarDiscriminationDataType>(crossPolarDiscriminationSearchHandler, appState => appState.performanceHistory.crossPolarDiscrimination); + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/deviceListActionHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/deviceListActionHandler.ts new file mode 100644 index 000000000..b3c0db446 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/deviceListActionHandler.ts @@ -0,0 +1,56 @@ +/** + * ============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 { IActionHandler } from '../../../../framework/src/flux/action'; + +import { AllDeviceListLoadedAction, LoadAllDeviceListAction } from '../actions/deviceListActions'; +import { DeviceListType } from '../models/deviceListType'; + +export interface IDeviceListState { + deviceList: DeviceListType[]; + busy: boolean; +} + +const deviceListStateInit: IDeviceListState = { + deviceList: [], + busy: false +}; + +export const deviceListActionHandler: IActionHandler<IDeviceListState> = (state = deviceListStateInit, action) => { + if (action instanceof LoadAllDeviceListAction) { + + state = { + ...state, + busy: true + }; + + } else if (action instanceof AllDeviceListLoadedAction) { + if (!action.error && action.deviceList) { + state = { + ...state, + deviceList: action.deviceList, + busy: false + }; + } else { + state = { + ...state, + busy: false + }; + } + } + return state; +};
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceDataHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceDataHandler.ts new file mode 100644 index 000000000..131566367 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceDataHandler.ts @@ -0,0 +1,40 @@ +/** + * ============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 moment from 'moment'; + +import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; +import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch'; + +import { PerformanceDataType } from '../models/performanceDataType'; +import { getFilter } from '../utils/tableUtils'; + +export interface IPerformanceDataState extends IExternalTableState<PerformanceDataType> { } + +/** +* Creates elastic search material data fetch handler for performance data from historicalperformance15min database. +*/ +const performanceDataSearchHandler = createSearchDataHandler<PerformanceDataType>(getFilter, null); + +export const { + actionHandler: performanceDataActionHandler, + createActions: createPerformanceDataActions, + createProperties: createPerformanceDataProperties, + createPreActions: createPerformanceDataPreActions, + reloadAction: performanceDataReloadAction +} = createExternal<PerformanceDataType>(performanceDataSearchHandler, appState => appState.performanceHistory.performanceData); + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts new file mode 100644 index 000000000..c33f10a7e --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts @@ -0,0 +1,105 @@ +/** + * ============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'; + +// ** do not remove ** +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { IActionHandler } from '../../../../framework/src/flux/action'; + +import { IConnectAppStoreState } from '../../../connectApp/src/handlers/connectAppRootHandler'; +import { IPerformanceDataState, performanceDataActionHandler } from './performanceDataHandler'; +import { IReceiveLevelState, receiveLevelActionHandler } from './receiveLevelHandler'; +import { ITransmissionPowerState, transmissionPowerActionHandler } from './transmissionPowerHandler'; +import { IAdaptiveModulationState, adaptiveModulationActionHandler } from './adaptiveModulationHandler'; +import { ITemperatureState, temperatureActionHandler } from './temperatureHandler'; +import { ISignalToInterferenceState, signalToInterferenceActionHandler } from './signalToInterferenceHandler'; +import { ICrossPolarDiscriminationState, crossPolarDiscriminationActionHandler } from './crossPolarDiscriminationHandler'; +import { SetPanelAction } from '../actions/panelChangeActions'; +import { IDeviceListState, deviceListActionHandler } from './deviceListActionHandler'; +import { IAvailableLtpsState, availableLtpsActionHandler } from './availableLtpsActionHandler'; +import { PmDataInterval } from '../models/performanceDataType'; +import { TimeChangeAction } from '../actions/timeChangeAction'; +import { UpdateMountId } from '../actions/deviceListActions'; + +export interface IPerformanceHistoryStoreState { + nodeId: string; + networkElements: IDeviceListState; + ltps: IAvailableLtpsState; + performanceData: IPerformanceDataState; + receiveLevel: IReceiveLevelState; + transmissionPower: ITransmissionPowerState; + adaptiveModulation: IAdaptiveModulationState; + temperature: ITemperatureState; + signalToInterference: ISignalToInterferenceState; + crossPolarDiscrimination: ICrossPolarDiscriminationState; + currentOpenPanel: string | null; + pmDataIntervalType: PmDataInterval; +} + +const mountIdHandler: IActionHandler<string> = (state = "", action) => { + if (action instanceof UpdateMountId) { + state = ""; + if (action.nodeId) { + state = action.nodeId; + } + } + return state; +} + + +const currentOpenPanelHandler: IActionHandler<string | null> = (state = null, action) => { + if (action instanceof SetPanelAction) { + state = action.panelId; + } + return state; +} + +const currentPMDataIntervalHandler: IActionHandler<PmDataInterval> = (state = PmDataInterval.pmInterval15Min, action) => { + if (action instanceof TimeChangeAction) { + state = action.time; + } + return state; +} + +declare module '../../../../framework/src/store/applicationStore' { + interface IApplicationStoreState { + performanceHistory: IPerformanceHistoryStoreState; + connect: IConnectAppStoreState; + } +} + +const actionHandlers = { + nodeId: mountIdHandler, + networkElements: deviceListActionHandler, + ltps: availableLtpsActionHandler, + performanceData: performanceDataActionHandler, + receiveLevel: receiveLevelActionHandler, + transmissionPower: transmissionPowerActionHandler, + adaptiveModulation: adaptiveModulationActionHandler, + temperature: temperatureActionHandler, + signalToInterference: signalToInterferenceActionHandler, + crossPolarDiscrimination: crossPolarDiscriminationActionHandler, + currentOpenPanel: currentOpenPanelHandler, + pmDataIntervalType: currentPMDataIntervalHandler +}; + +const performanceHistoryRootHandler = combineActionHandler<IPerformanceHistoryStoreState>(actionHandlers); +export default performanceHistoryRootHandler; + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/receiveLevelHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/receiveLevelHandler.ts new file mode 100644 index 000000000..91595cc58 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/receiveLevelHandler.ts @@ -0,0 +1,38 @@ +/** + * ============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 { ReceiveLevelDataType } from '../models/receiveLevelDataType'; +import { getFilter } from '../utils/tableUtils'; + +export interface IReceiveLevelState extends IExternalTableState<ReceiveLevelDataType> { } + +/** + * Creates elastic search material data fetch handler for receiveLevel from historicalperformance database. + */ +const receiveLevelSearchHandler = createSearchDataHandler<ReceiveLevelDataType>(getFilter, null); + +export const { + actionHandler: receiveLevelActionHandler, + createActions: createReceiveLevelActions, + createProperties: createReceiveLevelProperties, + createPreActions: createReceiveLevelPreActions, + reloadAction: receiveLevelReloadAction, +} = createExternal<ReceiveLevelDataType>(receiveLevelSearchHandler, appState => appState.performanceHistory.receiveLevel); + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/signalToInterferenceHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/signalToInterferenceHandler.ts new file mode 100644 index 000000000..e0f804086 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/signalToInterferenceHandler.ts @@ -0,0 +1,38 @@ +/** + * ============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 { SignalToInterferenceDataType } from '../models/signalToInteferenceDataType'; +import { getFilter } from '../utils/tableUtils'; + +export interface ISignalToInterferenceState extends IExternalTableState<SignalToInterferenceDataType> { } + +/** + * Creates elastic search material data fetch handler for SINR from historicalperformance database. + */ +const signalToInterferenceSearchHandler = createSearchDataHandler<SignalToInterferenceDataType>(getFilter, null); + +export const { + actionHandler: signalToInterferenceActionHandler, + createActions: createSignalToInterferenceActions, + createProperties: createSignalToInterferenceProperties, + createPreActions: createSignalToInterferencePreActions, + reloadAction: signalToInterferenceReloadAction, +} = createExternal<SignalToInterferenceDataType>(signalToInterferenceSearchHandler, appState => appState.performanceHistory.signalToInterference); + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperatureHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperatureHandler.ts new file mode 100644 index 000000000..0a6c73a1c --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperatureHandler.ts @@ -0,0 +1,38 @@ +/** + * ============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 { TemperatureDataType } from '../models/temperatureDataType'; +import { getFilter } from '../utils/tableUtils'; + +export interface ITemperatureState extends IExternalTableState<TemperatureDataType> { } + +/** + * Creates elastic search material data fetch handler for Temperature from historicalperformance database. + */ +const temperatureSearchHandler = createSearchDataHandler< TemperatureDataType>(getFilter, null); + +export const { + actionHandler: temperatureActionHandler, + createActions: createTemperatureActions, + createProperties: createTemperatureProperties, + createPreActions: createTemperaturePreActions, + reloadAction: temperatureReloadAction, +} = createExternal<TemperatureDataType>(temperatureSearchHandler, appState => appState.performanceHistory.temperature); + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPowerHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPowerHandler.ts new file mode 100644 index 000000000..32bef817d --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPowerHandler.ts @@ -0,0 +1,38 @@ +/** + * ============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 { TransmissionPowerDataType } from '../models/transmissionPowerDataType'; +import { getFilter } from '../utils/tableUtils'; + +export interface ITransmissionPowerState extends IExternalTableState<TransmissionPowerDataType> { } + +/** + * Creates elastic search material data fetch handler for Transmission power from historicalperformance database. + */ +const transmissionPowerSearchHandler = createSearchDataHandler<TransmissionPowerDataType>(getFilter, null) + +export const { + actionHandler: transmissionPowerActionHandler, + createActions: createTransmissionPowerActions, + createProperties: createTransmissionPowerProperties, + createPreActions: createTransmissionPowerPreActions, + reloadAction: transmissionPowerReloadAction, +} = createExternal<TransmissionPowerDataType>(transmissionPowerSearchHandler, appState => appState.performanceHistory.transmissionPower); + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/index.html b/sdnr/wt/odlux/apps/performanceHistoryApp/src/index.html new file mode 100644 index 000000000..8cb775be2 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/index.html @@ -0,0 +1,26 @@ +<!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>PM History Application</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", "connectApp", "performanceHistoryApp"], function (app, connectApp, performanceHistoryApp) { + connectApp.register(); + performanceHistoryApp.register(); + app("./app.tsx").runApplication(); + }); + </script> +</body> + +</html>
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/adaptiveModulationDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/adaptiveModulationDataType.ts new file mode 100644 index 000000000..adb0bcd6f --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/adaptiveModulationDataType.ts @@ -0,0 +1,109 @@ +/** + * ============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 { HitEntry, Result } from '../../../../framework/src/models'; + +/** + * Represents Adaptive Modulation data fields of the performance history table. + */ +export type AdaptiveModulationDatabaseDataType = { + _id: string ; + time2StatesS: number; + time2States: number; + time2StatesL: number; + time4StatesS: number; + time4States: number; + time4StatesL: number; + time16StatesS: number; + time16States: number; + time16StatesL: number; + time32StatesS: number; + time32States: number; + time32StatesL: number; + time64StatesS: number; + time64States: number; + time64StatesL: number; + time128StatesS: number; + time128States: number; + time128StatesL: number; + time256StatesS: number; + time256States: number; + time256StatesL: number; + time512StatesS: number; + time512States: number; + time512StatesL: number; + time1024StatesS: number; + time1024States: number; + time1024StatesL: number; + time2048StatesS: number; + time2048States: number; + time2048StatesL: number; + time4096StatesS: number; + time4096States: number; + time4096StatesL: number; + time8192StatesS: number; + time8192States: number; + time8192StatesL: number; +}; + + +/** + * Internally used type to provide table and chart data + */ +export type AdaptiveModulationDataType = { + performanceData: AdaptiveModulationDatabaseDataType; + radioSignalId: string; + scannerId: string; + timeStamp: string; + suspectIntervalFlag: boolean; + time2StatesS: number; + time2States: number; + time2StatesL: number; + time4StatesS: number; + time4States: number; + time4StatesL: number; + time16StatesS: number; + time16States: number; + time16StatesL: number; + time32StatesS: number; + time32States: number; + time32StatesL: number; + time64StatesS: number; + time64States: number; + time64StatesL: number; + time128StatesS: number; + time128States: number; + time128StatesL: number; + time256StatesS: number; + time256States: number; + time256StatesL: number; + time512StatesS: number; + time512States: number; + time512StatesL: number; + time1024StatesS: number; + time1024States: number; + time1024StatesL: number; + time2048StatesS: number; + time2048States: number; + time2048StatesL: number; + time4096StatesS: number; + time4096States: number; + time4096StatesL: number; + time8192StatesS: number; + time8192States: number; + time8192StatesL: number; +} & { _id: string };
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts new file mode 100644 index 000000000..dc6c7bc16 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts @@ -0,0 +1,21 @@ +/** + * ============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 LtpIds = { + key : string + } + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/chartTypes.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/chartTypes.ts new file mode 100644 index 000000000..53039faa2 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/chartTypes.ts @@ -0,0 +1,49 @@ +/** + * ============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 interface IData { + x: string; + y: string; +} + +/** + * Structure of chartjs dataset with the chart properties. + */ +export interface IDataSet { + name: string, + label: string, + lineTension: 0, + bezierCurve: boolean; + fill: boolean, + borderColor: string, + data: IData[], + columnLabel: string +} + +/** + * Structure of chartjs dataset which is sent to the chart. + */ +export interface IDataSetsObject { + datasets: IDataSet[] +} + +/** + * Interface used by chart for sorting on time-stamp + */ +export interface ITimeStamp { + timeStamp: string; +} diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts new file mode 100644 index 000000000..8adb16f45 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts @@ -0,0 +1,44 @@ +/** + * ============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 { HitEntry, Result } from '../../../../framework/src/models'; + + +/** + * Represents Receive level data fields of the performance history table. + */ +export type CrossPolarDiscriminationDatabaseDataType = { + _id: string; + xpdMin: number; + xpdAvg: number; + xpdMax: number; +}; + +/** + * Internally used type to provide table and chart data + */ +export type CrossPolarDiscriminationDataType = { + performanceData: CrossPolarDiscriminationDatabaseDataType + radioSignalId: string; + scannerId: string; + timeStamp: string; + suspectIntervalFlag: boolean; + xpdMin: number; + xpdAvg: number; + xpdMax: number; +} & { _id: string }; + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/deviceListType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/deviceListType.ts new file mode 100644 index 000000000..db8f2d71e --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/deviceListType.ts @@ -0,0 +1,25 @@ +/** + * ============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========================================================================== + */ + +/** + * Represents all the distinct devices from the performance history data. + */ + +export type DeviceListType = { + nodeId: string; +} diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/panelId.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/panelId.ts new file mode 100644 index 000000000..5889a64c8 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/panelId.ts @@ -0,0 +1,22 @@ +/** + * ============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========================================================================== + */ + +/** + * Represents PanelIds for the available Expansional panels. + */ +export type PanelId = null | "PerformanceData" | "ReceiveLevel" | "TransmissionPower" | "AdaptiveModulation" | "Temperature" | "SINR" | "CPD";
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts new file mode 100644 index 000000000..30f97fb54 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts @@ -0,0 +1,54 @@ +/** + * ============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 { Moment } from "moment"; + +//export { HitEntry, Result } from '../../../../framework/src/models'; + +/** + * Represents performance data fields of the performance history table as used in the database + */ +export type PerformanceDatabaseDataType = { + _id: string; + es: number; + ses: number; + unavailability: number; +}; + +/** + * Internally used type to provide table and chart data + */ +export type PerformanceDataType = { + + performanceData: PerformanceDatabaseDataType; + radioSignalId: string; + scannerId: string; + timeStamp: string; + suspectIntervalFlag: boolean; + es: number; + ses: number; + unavailability: number; +} & { _id: string }; + + +/** + * Represents performance data time interval. + */ +export const enum PmDataInterval { + pmInterval15Min, + pmInterval24Hours +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/receiveLevelDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/receiveLevelDataType.ts new file mode 100644 index 000000000..2748a3d5d --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/receiveLevelDataType.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========================================================================== + */ +export { HitEntry, Result } from '../../../../framework/src/models'; + +/** + * Represents Receive level data fields of the performance history table. + */ +export type ReceiveLevelDatabaseDataType = { + _id: string; + rxLevelMin: number; + rxLevelAvg: number; + rxLevelMax: number; +}; + +/** + * Internally used type to provide table and chart data + */ +export type ReceiveLevelDataType = { + performanceData: ReceiveLevelDatabaseDataType; + radioSignalId: string; + scannerId: string; + timeStamp: string; + suspectIntervalFlag: boolean; + rxLevelMin: number; + rxLevelAvg: number; + rxLevelMax: number; +} & { _id: string }; + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/signalToInteferenceDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/signalToInteferenceDataType.ts new file mode 100644 index 000000000..5c675feb8 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/signalToInteferenceDataType.ts @@ -0,0 +1,44 @@ +/** + * ============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 { HitEntry, Result } from '../../../../framework/src/models'; + + +/** + * Represents Receive level data fields of the performance history table. + */ +export type SignalToInterferenceDatabaseDataType = { + _id: string; + snirMin: number; + snirAvg: number; + snirMax: number; +}; + +/** + * Internally used type to provide table and chart data + */ +export type SignalToInterferenceDataType = { + performanceData: SignalToInterferenceDatabaseDataType; + radioSignalId: string; + scannerId: string; + timeStamp: string; + suspectIntervalFlag: boolean; + snirMin: number; + snirAvg: number; + snirMax: number; +} & { _id: string }; + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts new file mode 100644 index 000000000..3b0cb7683 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts @@ -0,0 +1,45 @@ +/** + * ============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 { HitEntry, Result } from '../../../../framework/src/models'; + + +/** + * Represents Receive level data fields of the performance history table. + */ +export type TemperatureDatabaseDataType = { + _id: string; + rfTempMin: number; + rfTempAvg: number; + rfTempMax: number; +}; + +/** + * Internally used type to provide table and chart data + */ +export type TemperatureDataType = { + performanceData: TemperatureDatabaseDataType + radioSignalId: string; + scannerId: string; + timeStamp: string; + suspectIntervalFlag: boolean; + rfTempMin: number; + rfTempAvg: number; + rfTempMax: number; +} & { _id: string }; + + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/topologyNetconf.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/topologyNetconf.ts new file mode 100644 index 000000000..99123f55e --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/topologyNetconf.ts @@ -0,0 +1,26 @@ +/** + * ============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 interface TopologyNode { + "node-id": string; +} + +export interface Topology { + "topology-id": string; + node: TopologyNode[]; +} diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/transmissionPowerDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/transmissionPowerDataType.ts new file mode 100644 index 000000000..62c00bfe4 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/transmissionPowerDataType.ts @@ -0,0 +1,44 @@ +/** + * ============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 { HitEntry, Result } from '../../../../framework/src/models'; + + +/** + * Represents Receive level data fields of the performance history table. + */ +export type TransmissionPowerDatabaseDataType = { + _id: string; + txLevelMin: number; + txLevelAvg: number; + txLevelMax: number; +}; + +/** + * Internally used type to provide table and chart data + */ +export type TransmissionPowerDataType = { + performanceData: TransmissionPowerDatabaseDataType; + radioSignalId: string; + scannerId: string; + timeStamp: string; + suspectIntervalFlag: boolean; + txLevelMin: number; + txLevelAvg: number; + txLevelMax: number; +} & { _id: string }; + diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/pluginPerformance.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/pluginPerformance.tsx new file mode 100644 index 000000000..df3ffd90a --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/pluginPerformance.tsx @@ -0,0 +1,111 @@ +/** + * ============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 { faBook } from '@fortawesome/free-solid-svg-icons'; + +import applicationManager from '../../../framework/src/services/applicationManager'; + +import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; +import performanceHistoryRootHandler from './handlers/performanceHistoryRootHandler'; +import { PmDataInterval } from './models/performanceDataType'; +import PerformanceHistoryApplication from './views/performanceHistoryApplication'; +import { ApplicationStore } from '../../../framework/src/store/applicationStore'; + +import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect'; +import { IApplicationStoreState } from "../../../framework/src/store/applicationStore"; +import { updateMountIdActionCreator } from "./actions/deviceListActions"; + +let api: { + readonly applicationStore: ApplicationStore | null; + readonly applicationStoreInitialized: Promise<ApplicationStore>; +} + +const mapProps = (state: IApplicationStoreState) => ({ +}); + +const mapDisp = (dispatcher: IDispatcher) => ({ + updateMountId: (mountId: string) => dispatcher.dispatch(updateMountIdActionCreator(mountId)) +}); + +let currentMountId: string | null = null; +let lastUrl: string = "/performanceHistory"; +const PerformanceHistoryApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ mountId?: string}> & Connect<typeof mapProps, typeof mapDisp>) => { + let mountId: string = ""; + if (props.location.pathname !== lastUrl) { + // ensure the asynchronus update will only be called once per path + lastUrl = props.location.pathname; + let index = lastUrl.lastIndexOf("performanceHistory/"); + if(index >= 0) { + mountId = lastUrl.substr(index+19); + } else { + mountId = ""; + } + + window.setTimeout(async () => { + // check if the mountId has changed + if (currentMountId !== mountId) { + currentMountId = mountId; + await props.updateMountId(currentMountId); + } + }); + } + return ( + <PerformanceHistoryApplication /> + ); +}); + +const PerformanceHistoryRouterApp = withRouter((props: RouteComponentProps) => { + props.history.action = "POP"; + return ( + <Switch> + <Route path={`${props.match.path}/:mountId`} component={PerformanceHistoryApplicationRouteAdapter} /> + <Route path={`${props.match.path}`} component={PerformanceHistoryApplicationRouteAdapter} /> + <Redirect to={`${props.match.path}`} /> + </Switch> + ) +}); + +export function register() { + api = applicationManager.registerApplication({ + name: "performanceHistory", + icon: faBook, + rootComponent: PerformanceHistoryRouterApp, + rootActionHandler: performanceHistoryRootHandler, + menuEntry: "Performance" + }); +} + +export function setPmDataInterval(pmDataInterval: PmDataInterval): boolean { + let reload: boolean = true; + if (api && api.applicationStore) { + if (api.applicationStore.state.performanceHistory.pmDataIntervalType !== pmDataInterval) { + reload = true; + } + api.applicationStore.state.performanceHistory.pmDataIntervalType = pmDataInterval; + } + return reload; +} + + +export function getPmDataInterval(): PmDataInterval { + let result = api && api.applicationStore + ? api.applicationStore.state.performanceHistory.pmDataIntervalType + : PmDataInterval.pmInterval15Min; + return result ? result : PmDataInterval.pmInterval15Min; +} diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts new file mode 100644 index 000000000..2b03d1c2e --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts @@ -0,0 +1,108 @@ +/** + * ============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 { requestRest } from '../../../../framework/src/services/restService'; +import { Result } from '../../../../framework/src/models/elasticSearch'; + +import { convertPropertyNames, replaceUpperCase } from '../../../../framework/src/utilities/yangHelper'; +import { LtpIds } from '../models/availableLtps'; +import { DeviceListType } from '../models/deviceListType'; +import { Topology, TopologyNode } from '../models/topologyNetconf'; + +/** + * Represents a web api accessor service for Network elements actions. + */ +class PerformanceService { + + /** + * Get distinct ltps based on the selected network element and time period from the historicalperformance15min database table. + */ + public async getDistinctLtpsFromDatabase(networkElement: string, selectedTimePeriod: string): Promise<LtpIds[] | null> { + let path; + const query = { + "filter": [{ + "property": "node-name", + "filtervalue": networkElement + }], + "sortorder": [], + "pagination": { + "size": 20, + "page": 1 + } + } + + + if (selectedTimePeriod === "15min") { + path = '/restconf/operations/data-provider:read-pmdata-15m-ltp-list'; + } else { + path = '/restconf/operations/data-provider:read-pmdata-24h-ltp-list'; + } + + const result = await requestRest<Result<string>>(path, { method: "POST", body: JSON.stringify(convertPropertyNames({ input: query }, replaceUpperCase)) }); + return result && result.output && result.output.data.map(ne => ({ key: ne })) || null; + } + + + + /** + * Gets all devices from the performanceHistory 15min backend. + */ + public async getDeviceListfromPerf15minHistory(): Promise<(DeviceListType)[] | null> { + const path = '/restconf/operations/data-provider:read-pmdata-15m-device-list'; + const query = { + "input": { + "filter": [], + "sortorder": [], + "pagination": { + "size": 20, + "page": 1 + } + } + }; + + const result = await requestRest<Result<string>>(path, { method: "POST", body: JSON.stringify(query) }); + return result && result.output && result.output.data && result.output.data.map(ne => ({ + nodeId: ne + })) || null; + } + + /** + * Gets all devices from the performanceHistory 24h backend. + */ + public async getDeviceListfromPerf24hHistory(): Promise<(DeviceListType)[] | null> { + const path = '/restconf/operations/data-provider:read-pmdata-24h-device-list'; + const query = { + "input": { + "filter": [], + "sortorder": [], + "pagination": { + "size": 20, + "page": 1 + } + } + }; + + const result = await requestRest<Result<string>>(path, { method: "POST", body: JSON.stringify(query) }); + return result && result.output && result.output.data && result.output.data.map(ne => ({ + nodeId: ne + })) || null; + } +} + + +export const PerformanceHistoryService = new PerformanceService(); +export default PerformanceHistoryService; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx new file mode 100644 index 000000000..3240f73f1 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx @@ -0,0 +1,75 @@ +/** + * ============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 { IDataSetsObject } from '../models/chartTypes'; +import { Line } from 'react-chartjs-2'; +import * as moment from 'moment'; +import { ITimeStamp } from 'models/chartTypes'; + +const style: React.CSSProperties = { + height: "350px" +} +export const lineChart = (chartPagedData: IDataSetsObject) => { + return ( + <div style={style}> + <Line ref="chart" data={chartPagedData} options={{ + responsive: true, + maintainAspectRatio: false, + scales: { + xAxes: [{ + type: 'time', + time: { + displayFormats: { + 'second': 'DD MMM YYYY HH:mm:ss', + 'minute': 'DD MMM YYYY HH:mm:ss', + 'hour': 'DD MMM YYYY HH:mm:ss', + 'year': 'DD MMM YYYY HH:mm:ss', + }, + parser: function (date: string) { + let offsetValue = new Date().getTimezoneOffset(); + var utcDate = moment(date, 'YYYY-MM-DDTHH:mm:ss').utcOffset(offsetValue).utc(false); + return utcDate; + } + }, + display: true, + scaleLabel: { + display: true, + labelString: 'Timestamp' + } + }], + yAxes: [{ + ticks: { + beginAtZero: true + }, + scaleLabel: { + display: true, + labelString: 'Value' + } + }] + } + }} /> + </div> + ); +} + +export const sortDataByTimeStamp = <T extends ITimeStamp>(_rows: T[]): T[] => { + return (_rows.sort((a, b) => { + const result = Date.parse(a["timeStamp"]) - Date.parse(b["timeStamp"]); + return isNaN(result) ? 0 : result; + })); +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/tableUtils.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/tableUtils.ts new file mode 100644 index 000000000..b5a3a3f36 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/tableUtils.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 { ColumnType, ColumnModel } from '../../../../framework/src/components/material-table'; + +import { PmDataInterval } from '../models/performanceDataType'; +import { getPmDataInterval } from '../pluginPerformance'; + +export const addColumnLabels = <T>(name: string, title: string, disableFilter = true, disableSorting = true): ColumnModel<T> => { + return { property: name as keyof T, title: title, type: ColumnType.text, disableFilter: disableFilter, disableSorting: disableSorting }; +} + +export function getFilter(): string { + switch (getPmDataInterval()) { + case PmDataInterval.pmInterval15Min: + return "pmdata-15m"; + case PmDataInterval.pmInterval24Hours: + return "pmdata-24h"; + default: + throw new Error("Unknown time intervall"); + } +}
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx new file mode 100644 index 000000000..ae22f8041 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx @@ -0,0 +1,371 @@ +/** + * ============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 { createStyles, Theme, withStyles, WithStyles } from '@material-ui/core/styles'; +import FormControl from '@material-ui/core/FormControl'; +import MenuItem from '@material-ui/core/MenuItem'; +import Select from '@material-ui/core/Select'; + +import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { Panel } from '../../../../framework/src/components/material-ui'; +import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions'; +import { Dispatch } from '../../../../framework/src/flux/store'; + +import { PanelId } from '../models/panelId'; +import { PmDataInterval } from '../models/performanceDataType'; +import PerformanceData from '../components/performanceData'; +import ReceiveLevel from '../components/receiveLevel'; +import TransmissionPower from '../components/transmissionPower'; +import AdaptiveModulation from '../components/adaptiveModulation'; +import Temperature from '../components/temperature'; +import SignalToInterference from '../components/signalToInterference'; +import CrossPolarDiscrimination from '../components/crossPolarDiscrimination'; +import { loadAllDeviceListAsync } from '../actions/deviceListActions'; +import { TimeChangeAction } from '../actions/timeChangeAction'; +import { loadDistinctLtpsbyNetworkElementAsync } from '../actions/ltpAction'; +import { SetPanelAction } from '../actions/panelChangeActions'; +import { createPerformanceDataPreActions, performanceDataReloadAction, createPerformanceDataActions } from '../handlers/performanceDataHandler'; +import { createReceiveLevelPreActions, receiveLevelReloadAction, createReceiveLevelActions } from '../handlers/receiveLevelHandler'; +import { createTransmissionPowerPreActions, transmissionPowerReloadAction, createTransmissionPowerActions } from '../handlers/transmissionPowerHandler'; +import { createAdaptiveModulationPreActions, adaptiveModulationReloadAction, createAdaptiveModulationActions } from '../handlers/adaptiveModulationHandler'; +import { createTemperaturePreActions, temperatureReloadAction, createTemperatureActions } from '../handlers/temperatureHandler'; +import { createSignalToInterferencePreActions, signalToInterferenceReloadAction, createSignalToInterferenceActions } from '../handlers/signalToInterferenceHandler'; +import { createCrossPolarDiscriminationPreActions, crossPolarDiscriminationReloadAction, createCrossPolarDiscriminationActions } from '../handlers/crossPolarDiscriminationHandler'; + +import { MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; + +const PerformanceHistoryComponentStyles = (theme: Theme) => createStyles({ + root: { + display: "flex", + flexWrap: "wrap", + }, + margin: { + margin: theme.spacing(1), + }, + display: { + display: "inline-block" + }, + selectDropdown: { + borderRadius: 1, + position: "relative", + backgroundColor: theme.palette.background.paper, + border: "1px solid #ced4da", + fontSize: 16, + width: "auto", + padding: "5px 26px 5px 12px", + transition: theme.transitions.create(["border-color", "box-shadow"]), + } +}); + +const mapProps = (state: IApplicationStoreState) => ({ + ...state.performanceHistory, + activePanel: state.performanceHistory.currentOpenPanel, + availableLtps: state.performanceHistory.ltps.distinctLtps, + networkElements: state.performanceHistory.networkElements.deviceList, +}); + +const mapDispatcher = (dispatcher: IDispatcher) => ({ + enableFilterPerformanceData: createPerformanceDataActions(dispatcher.dispatch), + enableFilterReceiveLevel: createReceiveLevelActions(dispatcher.dispatch), + enableFilterTransmissionPower: createTransmissionPowerActions(dispatcher.dispatch), + enableFilterAdaptiveModulation: createAdaptiveModulationActions(dispatcher.dispatch), + enableFilterTemperature: createTemperatureActions(dispatcher.dispatch), + enableFilterSinr: createSignalToInterferenceActions(dispatcher.dispatch), + enableFilterCpd: createCrossPolarDiscriminationActions(dispatcher.dispatch), + reloadPerformanceData: () => dispatcher.dispatch(performanceDataReloadAction), + reloadReceiveLevel: () => dispatcher.dispatch(receiveLevelReloadAction), + reloadTransmissionPower: () => dispatcher.dispatch(transmissionPowerReloadAction), + reloadAdaptiveModulation: () => dispatcher.dispatch(adaptiveModulationReloadAction), + reloadTemperature: () => dispatcher.dispatch(temperatureReloadAction), + reloadSignalToInterference: () => dispatcher.dispatch(signalToInterferenceReloadAction), + reloadCrossPolarDiscrimination: () => dispatcher.dispatch(crossPolarDiscriminationReloadAction), + performanceDataPreActions: createPerformanceDataPreActions(dispatcher.dispatch), + receiveLevelPreActions: createReceiveLevelPreActions(dispatcher.dispatch), + transmissionPowerPreActions: createTransmissionPowerPreActions(dispatcher.dispatch), + adaptiveModulationPreActions: createAdaptiveModulationPreActions(dispatcher.dispatch), + temperaturePreActions: createTemperaturePreActions(dispatcher.dispatch), + signalToInterferencePreActions: createSignalToInterferencePreActions(dispatcher.dispatch), + crossPolarDiscriminationPreActions: createCrossPolarDiscriminationPreActions(dispatcher.dispatch), + getAllDevicesPMdata: async () => { + await dispatcher.dispatch(loadAllDeviceListAsync); + }, + getDistinctLtpsIds: (selectedNetworkElement: string, selectedTimePeriod: string, selectedLtp: string, selectFirstLtp?: Function, resetLTP?: Function) => dispatcher.dispatch(loadDistinctLtpsbyNetworkElementAsync(selectedNetworkElement, selectedTimePeriod, selectedLtp, selectFirstLtp, resetLTP)), + setCurrentPanel: (panelId: PanelId) => dispatcher.dispatch(new SetPanelAction(panelId)), + timeIntervalChange: (time: PmDataInterval) => dispatcher.dispatch(new TimeChangeAction(time)), + changeNode: (nodeId: string) => dispatcher.dispatch((dispatch: Dispatch) => { + dispatch(new NavigateToApplication("performanceHistory", nodeId)); + }) +}); + +export type NetworkElementType = { + nodeId: string, +} +const NetworkElementTable = MaterialTable as MaterialTableCtorType<NetworkElementType>; + +type PerformanceHistoryComponentProps = Connect<typeof mapProps, typeof mapDispatcher> & WithStyles<typeof PerformanceHistoryComponentStyles>; + +type PerformanceHistoryComponentState = { + selectedNetworkElement: string, + selectedTimePeriod: string, + selectedLtp: string, + showNetworkElementsTable: boolean, + showLtps: boolean, + showPanels: boolean +}; + +/** +* Represents the component for Performance history application. +*/ +class PerformanceHistoryComponent extends React.Component<PerformanceHistoryComponentProps, PerformanceHistoryComponentState>{ + /** + * Initialises this instance + */ + constructor(props: PerformanceHistoryComponentProps) { + super(props); + this.state = { + selectedNetworkElement: "-1", + selectedTimePeriod: "15min", + selectedLtp: "-1", + showNetworkElementsTable: true, + showLtps: false, + showPanels: false + }; + } + + onTogglePanel = (panelId: PanelId) => { + const nextActivePanel = panelId === this.props.activePanel ? null : panelId; + this.props.setCurrentPanel(nextActivePanel); + switch (nextActivePanel) { + case "PerformanceData": + this.props.reloadPerformanceData(); + break; + case "ReceiveLevel": + this.props.reloadReceiveLevel(); + break; + case "TransmissionPower": + this.props.reloadTransmissionPower(); + break; + case "AdaptiveModulation": + this.props.reloadAdaptiveModulation(); + break; + case "Temperature": + this.props.reloadTemperature(); + break; + case "SINR": + this.props.reloadSignalToInterference(); + break; + case "CPD": + this.props.reloadCrossPolarDiscrimination(); + break; + default: + // do nothing if all panels are closed + break; + } + } + + render(): JSX.Element { + const { classes } = this.props; + const { activePanel, nodeId } = this.props; + if (nodeId === "") { + return ( + <> + <NetworkElementTable title={"Please select the network element!"} idProperty={"nodeId"} rows={this.props.networkElements} asynchronus + onHandleClick={(event, rowData) => { this.handleNetworkElementSelect(rowData.nodeId) }} columns={ + [{ property: "nodeId", title: "Node Name" }] + } /> + </> + ) + } + else { + 3 + this.handleURLChange(nodeId); + return ( + <> + <h3>Selected Network Element: {this.state.selectedNetworkElement} </h3> + {this.state.showLtps && ( + <div> + <FormControl className={classes.display}> + <span> + Select LTP + </span> + <Select className={classes.selectDropdown} value={this.state.selectedLtp} onChange={this.handleLtpChange} > + <MenuItem value={"-1"}><em>--Select--</em></MenuItem> + {this.props.availableLtps.map(ltp => + (<MenuItem value={ltp.key} key={ltp.key}>{ltp.key}</MenuItem>))} + </Select> + <span> Time-Period </span> + <Select className={classes.selectDropdown} value={this.state.selectedTimePeriod} onChange={this.handleTimePeriodChange} > + <MenuItem value={"15min"}>15min</MenuItem> + <MenuItem value={"24hours"}>24hours</MenuItem> + </Select> + </FormControl> + {this.state.showPanels && ( + <div> + <Panel activePanel={activePanel} panelId={"PerformanceData"} onToggle={this.onTogglePanel} title={"Performance Data"}> + <PerformanceData selectedTimePeriod={this.state.selectedTimePeriod} /> + </Panel> + <Panel activePanel={activePanel} panelId={"ReceiveLevel"} onToggle={this.onTogglePanel} title={"Receive Level"}> + <ReceiveLevel selectedTimePeriod={this.state.selectedTimePeriod} /> + </Panel> + <Panel activePanel={activePanel} panelId={"TransmissionPower"} onToggle={this.onTogglePanel} title={"Transmission Power"}> + <TransmissionPower selectedTimePeriod={this.state.selectedTimePeriod} /> + </Panel> + <Panel activePanel={activePanel} panelId={"AdaptiveModulation"} onToggle={this.onTogglePanel} title={"Adaptive Modulation"}> + <AdaptiveModulation selectedTimePeriod={this.state.selectedTimePeriod} /> + </Panel> + <Panel activePanel={activePanel} panelId={"Temperature"} onToggle={this.onTogglePanel} title={"Temperature"}> + <Temperature selectedTimePeriod={this.state.selectedTimePeriod} /> + </Panel> + <Panel activePanel={activePanel} panelId={"SINR"} onToggle={this.onTogglePanel} title={"Signal-to-interference-plus-noise ratio (SINR)"}> + <SignalToInterference selectedTimePeriod={this.state.selectedTimePeriod} /> + </Panel> + <Panel activePanel={activePanel} panelId={"CPD"} onToggle={this.onTogglePanel} title={"Cross Polar Discrimination"}> + <CrossPolarDiscrimination selectedTimePeriod={this.state.selectedTimePeriod} /> + </Panel> + </div> + )} + </div> + )} + </> + ); + } + }; + + public componentDidMount() { + this.props.getAllDevicesPMdata(); + this.props.enableFilterPerformanceData.onToggleFilter(); + this.props.enableFilterReceiveLevel.onToggleFilter(); + this.props.enableFilterTransmissionPower.onToggleFilter(); + this.props.enableFilterTemperature.onToggleFilter(); + this.props.enableFilterAdaptiveModulation.onToggleFilter(); + this.props.enableFilterSinr.onToggleFilter(); + this.props.enableFilterCpd.onToggleFilter(); + } + + /** + * Function which selects the first ltp returned from the database on selection of network element. + */ + private selectFirstLtp = (firstLtp: string) => { + this.setState({ + showPanels: true, + selectedLtp: firstLtp + }); + this.preFilterChangeAndReload(this.state.selectedNetworkElement, this.state.selectedTimePeriod, firstLtp); + } + + /** + * A function which loads the tables based on prefilters defined by network element and ltp on selected time period. + */ + private preFilterChangeAndReload = (networkElement: string, timePeriod: string, ltp: string) => { + const preFilter = { + "node-name": networkElement, + "uuid-interface": ltp + }; + this.props.performanceDataPreActions.onPreFilterChanged(preFilter); + this.props.receiveLevelPreActions.onPreFilterChanged(preFilter); + this.props.transmissionPowerPreActions.onPreFilterChanged(preFilter); + this.props.adaptiveModulationPreActions.onPreFilterChanged(preFilter); + this.props.temperaturePreActions.onPreFilterChanged(preFilter); + this.props.signalToInterferencePreActions.onPreFilterChanged(preFilter); + this.props.crossPolarDiscriminationPreActions.onPreFilterChanged(preFilter); + + } + + /** + * Function which handles network element changes. + */ + private handleNetworkElementSelect = (selectedNetworkElement: string) => { + + this.setState({ + showLtps: true, + selectedNetworkElement: selectedNetworkElement, + showNetworkElementsTable: false, + showPanels: false, + selectedLtp: "-1" + }); + this.props.changeNode(selectedNetworkElement); + this.props.getDistinctLtpsIds(selectedNetworkElement, this.state.selectedTimePeriod, "-1", this.selectFirstLtp); + } + + private handleURLChange = (selectedNetworkElement: string) => { + if (selectedNetworkElement !== this.state.selectedNetworkElement) { + this.setState({ + showLtps: true, + selectedNetworkElement: selectedNetworkElement, + showPanels: false, + selectedLtp: "-1" + }); + this.props.getDistinctLtpsIds(selectedNetworkElement, this.state.selectedTimePeriod, "-1", this.selectFirstLtp); + } + } + + /** + * Function which resets the ltps to "--select--" in the state if the passed parameter @ltpNotSelected is true. + * @param ltpNotSelected: true, if existing selected is not available in the given time period, else false + */ + private resetLtpDropdown = (ltpNotSelected: boolean) => { + if (ltpNotSelected) { + this.setState({ + selectedLtp: "-1", + }); + } + } + + /** + * Function which handles the time period changes. + */ + private handleTimePeriodChange = (event: React.ChangeEvent<HTMLSelectElement>) => { + const selectedTimeInterval = event.target.value === "15min" + ? PmDataInterval.pmInterval15Min + : PmDataInterval.pmInterval24Hours + + this.setState({ + selectedTimePeriod: event.target.value, + }); + this.props.timeIntervalChange(selectedTimeInterval); + this.props.reloadPerformanceData(); + this.props.reloadReceiveLevel(); + this.props.reloadTransmissionPower(); + this.props.reloadAdaptiveModulation(); + this.props.reloadTemperature(); + this.props.reloadSignalToInterference(); + this.props.reloadCrossPolarDiscrimination(); + this.props.getDistinctLtpsIds(this.state.selectedNetworkElement, event.target.value, this.state.selectedLtp, undefined, this.resetLtpDropdown); + this.preFilterChangeAndReload(this.state.selectedNetworkElement, event.target.value, this.state.selectedLtp); + } + + /** + * Function which handles the ltp changes. + */ + private handleLtpChange = (event: React.ChangeEvent<HTMLSelectElement>) => { + var showPanels: boolean = true; + if (event.target.value === "-1") { + showPanels = false; + } + this.setState({ + showPanels: showPanels, + selectedLtp: event.target.value + }); + this.preFilterChangeAndReload(this.state.selectedNetworkElement, this.state.selectedTimePeriod, event.target.value); + } +} + +const PerformanceHistoryApplication = withStyles(PerformanceHistoryComponentStyles)(connect(mapProps, mapDispatcher)(PerformanceHistoryComponent)); +export default PerformanceHistoryApplication; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java b/sdnr/wt/odlux/apps/performanceHistoryApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java new file mode 100644 index 000000000..1e882fc69 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src2/main/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/MyOdluxBundle.java @@ -0,0 +1,68 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles; + +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundle; +import org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader; + +public class MyOdluxBundle extends OdluxBundle { + + @Override + public void initialize() { + super.initialize(); + } + + @Override + public void clean() { + super.clean(); + } + + @Override + public String getResourceFileContent(String filename) { + return super.getResourceFileContent(filename); + } + + @Override + public boolean hasResource(String filename) { + return super.hasResource(filename); + } + + @Override + public void setBundleName(String bundleName) { + super.setBundleName(bundleName); + } + + @Override + public void setLoader(OdluxBundleLoader loader) { + super.setLoader(loader); + } + + @Override + public String getBundleName() { + return super.getBundleName(); + } + + @Override + public OdluxBundleLoader getLoader() { + return super.getLoader(); + } + + public MyOdluxBundle() { + super(); + } +} diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml b/sdnr/wt/odlux/apps/performanceHistoryApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml new file mode 100644 index 000000000..1bcbf0127 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src2/main/resources/OSGI-INF/blueprint/blueprint.xml @@ -0,0 +1,9 @@ +<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"> + <reference id="loadersvc" availability="mandatory" activation="eager" interface="org.onap.ccsdk.features.sdnr.wt.odlux.model.bundles.OdluxBundleLoader"/> + + <bean id="bundle" init-method="initialize" destroy-method="clean" class="org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle"> + <property name="loader" ref="loadersvc"/> + <property name="bundleName" value="performanceHistoryApp"/> + <property name="index" value="55"/> + </bean> +</blueprint> diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java b/sdnr/wt/odlux/apps/performanceHistoryApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java new file mode 100644 index 000000000..edf68e096 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src2/test/java/org/onap/ccsdk/features/sdnr/wt/odlux/bundles/test/TestBundleRes.java @@ -0,0 +1,45 @@ +/******************************************************************************* + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt + * ================================================================================================= + * 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========================================================================== + ******************************************************************************/ +package org.onap.ccsdk.features.sdnr.wt.odlux.bundles.test; + +import static org.junit.Assert.*; + +import org.junit.Test; +import org.onap.ccsdk.features.sdnr.wt.odlux.OdluxBundleLoaderImpl; +import org.onap.ccsdk.features.sdnr.wt.odlux.bundles.MyOdluxBundle; + +public class TestBundleRes { + + @Test + public void test() { + OdluxBundleLoaderImpl loader = OdluxBundleLoaderImpl.getInstance(); + MyOdluxBundle b = new MyOdluxBundle(); + b.setLoader(loader); + b.setIndex(0); + b.setBundleName("abc"); + b.initialize(); + assertTrue(loader.getNumberOfBundles()==1); + assertNotNull(b.getLoader()); + assertEquals("abc",b.getBundleName()); + assertTrue(b.hasResource("test.js")); + assertNotNull(b.getResourceFileContent("test.js")); + b.clean(); + assertTrue(loader.getNumberOfBundles()==0); + } + +} diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src2/test/resources/test.js b/sdnr/wt/odlux/apps/performanceHistoryApp/src2/test/resources/test.js new file mode 100644 index 000000000..b47fdc39f --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src2/test/resources/test.js @@ -0,0 +1,5 @@ +asdac sad +as +d +sad + sadfa
\ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/tsconfig.json b/sdnr/wt/odlux/apps/performanceHistoryApp/tsconfig.json new file mode 100644 index 000000000..a66b5d828 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/tsconfig.json @@ -0,0 +1,37 @@ +{ + "compilerOptions": { + "baseUrl": "./src", + "outDir": "./dist", + "sourceMap": true, + "forceConsistentCasingInFileNames": true, + "allowSyntheticDefaultImports": false, + "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": [ + "prop-types", + "react", + "react-dom" + ] + }, + "exclude": [ + "dist", + "node_modules" + ] +} diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/webpack.config.js b/sdnr/wt/odlux/apps/performanceHistoryApp/webpack.config.js new file mode 100644 index 000000000..9c2414864 --- /dev/null +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/webpack.config.js @@ -0,0 +1,143 @@ +/** + * 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: { + performanceHistoryApp: ["./pluginPerformance.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" + }] + }] + }, + + 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://10.20.6.29:48181", + secure: false + }, + "/restconf": { + target: "http://10.20.6.29:48181", + secure: false + }, + "/database": { + target: "http://10.20.6.29:48181", + secure: false + } + } + } + }]; +} |