update odlux and featureaggregator
v2 update odlux and featureaggregator bundles
Issue-ID: SDNC-1008
Signed-off-by: herbert <herbert.eiselt@highstreet-technologies.com>
Change-Id: I0018d7bfa3a0e6896c1b210b539a574af9808e22
Signed-off-by: herbert <herbert.eiselt@highstreet-technologies.com>
diff --git a/sdnr/wt/odlux/apps/apiDemo/.babelrc b/sdnr/wt/odlux/apps/apiDemo/.babelrc
new file mode 100644
index 0000000..3d8cd12
--- /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 0000000..d6f7427
--- /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 0000000..11bf306
--- /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 0000000..12fd3fc
--- /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 0000000..36688b3
--- /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 0000000..8777355
--- /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 0000000..c01df6b
--- /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 0000000..abf86b7
--- /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 0000000..6790ee9
--- /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 0000000..1e882fc
--- /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 0000000..0877578
--- /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 0000000..e2ffb59
--- /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 0000000..b47fdc3
--- /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 0000000..a66b5d8
--- /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 0000000..1c699e1
--- /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 0000000..dd67901
--- /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 0000000..20871a4
--- /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 0000000..c4eb9aa
--- /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 0000000..3d8cd12
--- /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 0000000..2985ba4
--- /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 0000000..a4e0678
--- /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 0000000..fc06653
--- /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 0000000..04b63d3
--- /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 0000000..6a68242
--- /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 0000000..3cc27aa
--- /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 0000000..c9e7dd2
--- /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 0000000..48155ee
--- /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 0000000..759b7b5
--- /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 0000000..2575500
--- /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 0000000..441d128
--- /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 0000000..57edf80
--- /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 0000000..7fd3a97
--- /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 0000000..061be05
--- /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 0000000..17a4e43
--- /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 0000000..24a4af8
--- /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 0000000..6fd5c8c
--- /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 0000000..c7ab5e4
--- /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 0000000..1e882fc
--- /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 0000000..48f7dde
--- /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 0000000..edf68e0
--- /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 0000000..b47fdc3
--- /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 0000000..b0c9b42
--- /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 0000000..6a5c5d5
--- /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 0000000..3d8cd12
--- /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 0000000..04653d5
--- /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 0000000..f52fa11
--- /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 0000000..bf4778b
--- /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 0000000..4ae28aa
--- /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 0000000..84e73ae
--- /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 0000000..1a86f94
--- /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 0000000..4e5ca65
--- /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 0000000..1e1f115
--- /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 0000000..ea9d419
--- /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 0000000..0f4b0e8
--- /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 0000000..1440599
--- /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 0000000..6863ec3
--- /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 0000000..d67a81e
--- /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 0000000..78c7000
--- /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 0000000..9c8a206
--- /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 0000000..df43c21
--- /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 0000000..4b443ba
--- /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 0000000..39278a8
--- /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 0000000..4c2dc9b
--- /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 0000000..a1535cb
--- /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 0000000..b514120
--- /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 0000000..694009d
--- /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 0000000..2304682
--- /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 0000000..f711c44
--- /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 0000000..7a410f4
--- /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 0000000..aa3391c
--- /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 0000000..1e882fc
--- /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 0000000..adc9b77
--- /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 0000000..edf68e0
--- /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 0000000..b47fdc3
--- /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 0000000..a66b5d8
--- /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 0000000..6d70b04
--- /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 0000000..3d8cd12
--- /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 0000000..f692d69
--- /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 0000000..8cffb9e
--- /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 0000000..f750751
--- /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 0000000..b47fa45
--- /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 0000000..9ff8450
--- /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 0000000..34b533c
--- /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 0000000..ca2b6d3
--- /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 0000000..e85bcbb
--- /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 0000000..0aaa308
--- /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 0000000..4d67c28
--- /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 0000000..13e4b31
--- /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 0000000..3d0651b
--- /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 0000000..92f6712
--- /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 0000000..1e882fc
--- /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 0000000..91f6002
--- /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 0000000..edf68e0
--- /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 0000000..b47fdc3
--- /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 0000000..a66b5d8
--- /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 0000000..61dd8f6
--- /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 0000000..3d8cd12
--- /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 0000000..2594deb
--- /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 0000000..9e369ee
--- /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 0000000..6e2d40e
--- /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 0000000..300a8f7
--- /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 0000000..8027509
--- /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 0000000..7c68e64
--- /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 0000000..a2edb43
--- /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 0000000..535de1f
--- /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 0000000..1e882fc
--- /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 0000000..cb2d062
--- /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 0000000..e2ffb59
--- /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 0000000..b47fdc3
--- /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 0000000..a66b5d8
--- /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 0000000..a67a340
--- /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 0000000..3d8cd12
--- /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 0000000..9096263
--- /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 0000000..973c106
--- /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 0000000..ba1a248
--- /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 0000000..584e7cd
--- /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 0000000..f032c13
--- /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 0000000..8a36332
--- /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 0000000..3b8b9b6
--- /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 0000000..1ec463f
--- /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 0000000..31b8259
--- /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 0000000..14634b4
--- /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 0000000..3698a27
--- /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 0000000..dddb4a2
--- /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 0000000..1c76a4b
--- /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 0000000..aa92d2a
--- /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 0000000..cd315f0
--- /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 0000000..c657344
--- /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 0000000..186aa53
--- /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 0000000..02dde90
--- /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 0000000..d3409e0
--- /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 0000000..fd1c48a
--- /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 0000000..1e882fc
--- /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 0000000..e7c262b
--- /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 0000000..edf68e0
--- /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 0000000..b47fdc3
--- /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 0000000..a66b5d8
--- /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 0000000..66926ed
--- /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 0000000..3d8cd12
--- /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 0000000..1f106ab
--- /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 0000000..8180a68
--- /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 0000000..3cebfd6
--- /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 0000000..1409357
--- /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 0000000..a713783
--- /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 0000000..fc7b8df
--- /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 0000000..cc6a984
--- /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 0000000..2344708
--- /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 0000000..dbefeec
--- /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 0000000..50a264b
--- /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 0000000..c8b2478
--- /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 0000000..412bdfb
--- /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 0000000..b4de26d
--- /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 0000000..65d4609
--- /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 0000000..1e882fc
--- /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 0000000..a0e3dac
--- /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 0000000..d0fbe63
--- /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 0000000..b47fdc3
--- /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 0000000..a66b5d8
--- /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 0000000..14ab528
--- /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 0000000..3d8cd12
--- /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 0000000..9e004c1
--- /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 0000000..081f070
--- /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 0000000..786f6d0
--- /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 0000000..a65319e
--- /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 0000000..0cdb9e9
--- /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 0000000..9d74741
--- /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 0000000..ad53285
--- /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 0000000..bd182ed
--- /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 0000000..f03b30c
--- /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 0000000..1e882fc
--- /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 0000000..f0bf054
--- /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 0000000..d0fbe63
--- /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 0000000..b47fdc3
--- /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 0000000..a66b5d8
--- /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 0000000..4267638
--- /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 0000000..3d8cd12
--- /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 0000000..96f9f7d
--- /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 0000000..be6e2ea
--- /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 0000000..162d943
--- /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 0000000..e0cd514
--- /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 0000000..71b4bf7
--- /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 0000000..4455418
--- /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 0000000..c84aece
--- /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 0000000..989bfbd
--- /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 0000000..e6ab977
--- /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 0000000..daa6082
--- /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 0000000..676be1a
--- /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 0000000..3b78793
--- /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 0000000..1e882fc
--- /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 0000000..2dd79d7
--- /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 0000000..d0fbe63
--- /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 0000000..b47fdc3
--- /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 0000000..a66b5d8
--- /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 0000000..e6d9685
--- /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 0000000..3d8cd12
--- /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 0000000..718d2fd
--- /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 0000000..bc3355a
--- /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 0000000..3f56b05
--- /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 0000000..516515a
--- /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 0000000..79e46df
--- /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 0000000..bcbcccc
--- /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 0000000..882a6b3
--- /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 0000000..fd8a249
--- /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 0000000..c22252d
--- /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 0000000..2642ec8
--- /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 0000000..246634c
--- /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 0000000..95bf9ec
--- /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 0000000..6ab6db8
--- /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 0000000..5ffd012
--- /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 0000000..aee0866
--- /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 0000000..945e135
--- /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 0000000..a5b34a2
--- /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 0000000..1e882fc
--- /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 0000000..c11492b
--- /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 0000000..d0fbe63
--- /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 0000000..b47fdc3
--- /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 0000000..b0c9b42
--- /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 0000000..b5143e9
--- /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 0000000..3efe2d0
--- /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 0000000..3d8cd12
--- /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 0000000..d9b18a3
--- /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 0000000..dc9507c
--- /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 0000000..463ad21
--- /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 0000000..58865ed
--- /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 0000000..3095ba7
--- /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 0000000..1e882fc
--- /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 0000000..4c8ed13
--- /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 0000000..d0fbe63
--- /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 0000000..b47fdc3
--- /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 0000000..a66b5d8
--- /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 0000000..64a5344
--- /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 0000000..3d8cd12
--- /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 0000000..1e6adf7
--- /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 0000000..b6ede0c
--- /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 0000000..9637fec
--- /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 0000000..2741d88
--- /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 0000000..8b77cb3
--- /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 0000000..a069af1
--- /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 0000000..09d3acc
--- /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 0000000..267e00b
--- /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 0000000..c58c49c
--- /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 0000000..55bc398
--- /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 0000000..42aa007
--- /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 0000000..256911c
--- /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 0000000..635cbf1
--- /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 0000000..3548bf4
--- /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 0000000..b3b284b
--- /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 0000000..1e6c6d0
--- /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 0000000..b3c0db4
--- /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 0000000..1315663
--- /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 0000000..c33f10a
--- /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 0000000..91595cc
--- /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 0000000..e0f8040
--- /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 0000000..0a6c73a
--- /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 0000000..32bef81
--- /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 0000000..8cb775b
--- /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 0000000..adb0bcd
--- /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 0000000..dc6c7bc
--- /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 0000000..53039fa
--- /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 0000000..8adb16f
--- /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 0000000..db8f2d7
--- /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 0000000..5889a64
--- /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 0000000..30f97fb
--- /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 0000000..2748a3d
--- /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 0000000..5c675fe
--- /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 0000000..3b0cb76
--- /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 0000000..99123f5
--- /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 0000000..62c00bf
--- /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 0000000..df3ffd9
--- /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 0000000..2b03d1c
--- /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 0000000..3240f73
--- /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 0000000..b5a3a3f
--- /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 0000000..ae22f80
--- /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 0000000..1e882fc
--- /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 0000000..1bcbf01
--- /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 0000000..edf68e0
--- /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 0000000..b47fdc3
--- /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 0000000..a66b5d8
--- /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 0000000..9c24148
--- /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
+ }
+ }
+ }
+ }];
+}