From 15e2d3a29b0d1a304965e34f114a911e5a7abdb3 Mon Sep 17 00:00:00 2001 From: sai-neetha Date: Mon, 20 Mar 2023 08:05:47 +0100 Subject: Odlux Update Add eslint and custom icons update Issue-ID: CCSDK-3871 Signed-off-by: sai-neetha Change-Id: If6b676128cc9cff0437a5dc54f85eaafd3b8c586 Signed-off-by: highstreetherbert --- docs/guides/onap-user/mwtnLog.rst | 10 - docs/guides/onap-user/mwtnTest.rst | 9 - docs/guides/onap-user/pnfEventLog.rst | 11 - docs/guides/onap-user/sdnr.rst | 22 - docs/tox.ini | 4 +- sdnr/wt/odlux/.eslintignore | 12 + sdnr/wt/odlux/apps/apiDemo/package.json | 3 + sdnr/wt/odlux/apps/apiDemo/pom.xml | 3 +- .../apiDemo/src/handlers/apiDemoRootHandler.ts | 7 +- .../apps/apiDemo/src/handlers/modulesHandler.ts | 2 +- sdnr/wt/odlux/apps/apiDemo/src/models/module.ts | 8 +- sdnr/wt/odlux/apps/apiDemo/src/plugin.tsx | 18 +- sdnr/wt/odlux/apps/apiDemo/tsconfig.json | 2 +- sdnr/wt/odlux/apps/configurationApp/package.json | 6 +- sdnr/wt/odlux/apps/configurationApp/pom.xml | 3 +- .../configurationApp/src/actions/deviceActions.ts | 498 ++---- .../src/assets/icons/configurationAppIcon.svg | 20 + .../configurationApp/src/components/baseProps.ts | 14 +- .../src/components/ifWhenTextInput.tsx | 72 +- .../src/components/uiElementBoolean.tsx | 41 +- .../src/components/uiElementLeafList.tsx | 170 +- .../src/components/uiElementNumber.tsx | 12 +- .../src/components/uiElementReference.tsx | 35 +- .../src/components/uiElementSelection.tsx | 51 +- .../src/components/uiElementString.tsx | 4 +- .../src/components/uiElementUnion.tsx | 4 +- .../configurationApp/src/components/verifyer.ts | 280 ---- .../src/handlers/configurationAppRootHandler.ts | 8 +- .../handlers/connectedNetworkElementsHandler.ts | 6 +- .../src/handlers/deviceDescriptionHandler.ts | 22 +- .../src/handlers/valueSelectorHandler.ts | 20 +- .../src/handlers/viewDescriptionHandler.ts | 20 +- .../src/models/networkElementConnection.ts | 6 +- .../apps/configurationApp/src/models/uiModels.ts | 256 +-- .../odlux/apps/configurationApp/src/models/yang.ts | 28 +- .../configurationApp/src/pluginConfiguration.tsx | 69 +- .../configurationApp/src/services/restServices.ts | 142 +- .../configurationApp/src/services/yangService.ts | 34 +- .../configurationApp/src/utilities/verifyer.ts | 261 +++ .../src/utilities/viewEngineHelper.ts | 324 ++++ .../src/views/configurationApplication.tsx | 320 ++-- .../src/views/networkElementSelector.tsx | 28 +- .../apps/configurationApp/src/yang/whenParser.ts | 235 +++ .../apps/configurationApp/src/yang/yangParser.ts | 844 +++++----- .../odlux/apps/configurationApp/webpack.config.js | 62 +- sdnr/wt/odlux/apps/connectApp/package.json | 1 + sdnr/wt/odlux/apps/connectApp/pom.xml | 3 +- .../src/actions/commonNetworkElementsActions.ts | 40 +- .../src/actions/infoNetworkElementActions.ts | 80 +- .../src/actions/mountedNetworkElementsActions.ts | 2 +- .../src/actions/networkElementsActions.ts | 13 +- .../apps/connectApp/src/actions/tlsKeyActions.ts | 21 +- .../connectApp/src/assets/icons/connectAppIcon.svg | 22 + .../src/components/connectionStatusLog.tsx | 39 +- .../src/components/editNetworkElementDialog.tsx | 209 +-- .../src/components/infoNetworkElementDialog.tsx | 285 ++-- .../connectApp/src/components/networkElements.tsx | 174 +- .../refreshConnectionStatusLogDialog.tsx | 61 +- .../components/refreshNetworkElementsDialog.tsx | 61 +- .../src/handlers/connectAppRootHandler.ts | 25 +- .../src/handlers/connectionStatusLogHandler.ts | 3 +- .../src/handlers/infoNetworkElementHandler.ts | 137 +- .../src/handlers/networkElementsHandler.ts | 13 +- .../apps/connectApp/src/handlers/tlsKeyHandler.ts | 4 +- .../connectApp/src/models/connectionStatusLog.ts | 2 +- .../apps/connectApp/src/models/guiCutTrough.ts | 6 +- .../connectApp/src/models/networkElementBase.ts | 8 +- .../src/models/networkElementConnection.ts | 16 +- .../src/models/networkElementConnectionLog.ts | 4 +- .../wt/odlux/apps/connectApp/src/models/panelId.ts | 2 +- .../apps/connectApp/src/models/topologyNetconf.ts | 20 +- .../connectApp/src/models/yangCapabilitiesType.ts | 12 +- .../wt/odlux/apps/connectApp/src/pluginConnect.tsx | 47 +- .../apps/connectApp/src/services/connectService.ts | 135 +- .../apps/connectApp/src/views/connectView.tsx | 141 +- sdnr/wt/odlux/apps/connectApp/tsconfig.json | 2 +- sdnr/wt/odlux/apps/connectApp/webpack.config.js | 10 + sdnr/wt/odlux/apps/demoApp/package.json | 3 + sdnr/wt/odlux/apps/demoApp/pom.xml | 3 +- .../apps/demoApp/src/actions/authorActions.ts | 7 +- .../odlux/apps/demoApp/src/components/counter.tsx | 25 +- .../demoApp/src/handlers/demoAppRootHandler.ts | 3 +- .../apps/demoApp/src/handlers/editAuthorHandler.ts | 4 +- .../demoApp/src/handlers/listAuthorsHandler.ts | 8 +- sdnr/wt/odlux/apps/demoApp/src/models/author.ts | 2 +- sdnr/wt/odlux/apps/demoApp/src/plugin.tsx | 12 +- .../apps/demoApp/src/services/authorService.ts | 18 +- .../odlux/apps/demoApp/src/views/authorsList.tsx | 32 +- .../wt/odlux/apps/demoApp/src/views/editAuthor.tsx | 6 +- sdnr/wt/odlux/apps/demoApp/tsconfig.json | 2 +- sdnr/wt/odlux/apps/eventLogApp/pom.xml | 3 +- .../src/assets/icons/eventLogAppIcon.svg | 21 + .../odlux/apps/eventLogApp/src/pluginEventLog.tsx | 16 +- sdnr/wt/odlux/apps/eventLogApp/tsconfig.json | 2 +- sdnr/wt/odlux/apps/eventLogApp/webpack.config.js | 10 + sdnr/wt/odlux/apps/faultApp/package.json | 7 +- sdnr/wt/odlux/apps/faultApp/pom.xml | 3 +- .../faultApp/src/actions/clearStuckAlarmsAction.ts | 23 +- .../faultApp/src/actions/panelChangeActions.ts | 3 +- .../apps/faultApp/src/actions/statusActions.ts | 38 +- .../faultApp/src/assets/icons/faultAppIcon.svg | 19 + .../src/components/clearStuckAlarmsDialog.tsx | 203 +-- .../apps/faultApp/src/components/dashboardHome.tsx | 169 +- .../apps/faultApp/src/components/faultStatus.tsx | 29 +- .../src/components/refreshAlarmLogDialog.tsx | 59 +- .../src/components/refreshCurrentAlarmsDialog.tsx | 113 ++ .../components/refreshCurrentProblemsDialog.tsx | 117 -- .../src/handlers/alarmLogEntriesHandler.ts | 2 +- .../src/handlers/clearStuckAlarmsHandler.ts | 21 +- .../faultApp/src/handlers/currentAlarmsHandler.ts | 36 + .../src/handlers/currentProblemsHandler.ts | 36 - .../faultApp/src/handlers/faultAppRootHandler.ts | 19 +- .../faultApp/src/handlers/faultStatusHandler.ts | 41 +- .../faultApp/src/handlers/notificationsHandler.ts | 11 +- sdnr/wt/odlux/apps/faultApp/src/models/fault.ts | 86 +- sdnr/wt/odlux/apps/faultApp/src/models/panelId.ts | 2 +- sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx | 112 +- .../faultApp/src/services/faultStatusService.ts | 44 +- .../apps/faultApp/src/views/faultApplication.tsx | 185 +-- sdnr/wt/odlux/apps/faultApp/tsconfig.json | 2 +- sdnr/wt/odlux/apps/faultApp/webpack.config.js | 22 +- sdnr/wt/odlux/apps/helpApp/pom.xml | 3 +- .../apps/helpApp/src/assets/icons/helpAppIcon.svg | 27 + .../apps/helpApp/src/components/helpStatus.tsx | 10 +- .../helpApp/src/handlers/helpAppRootHandler.ts | 3 +- sdnr/wt/odlux/apps/helpApp/src/plugin.tsx | 16 +- .../apps/helpApp/src/views/helpApplication.tsx | 4 +- .../wt/odlux/apps/helpApp/src/views/helpTocApp.tsx | 4 +- sdnr/wt/odlux/apps/helpApp/tsconfig.json | 2 +- sdnr/wt/odlux/apps/helpApp/webpack.config.js | 10 + sdnr/wt/odlux/apps/inventoryApp/pom.xml | 3 +- .../src/actions/inventoryDeviceListActions.ts | 59 + .../src/actions/inventoryTreeActions.ts | 33 +- .../apps/inventoryApp/src/actions/panelActions.ts | 14 +- .../src/assets/icons/inventoryAppIcon.svg | 23 + .../src/components/refreshInventoryDialog.tsx | 58 +- .../odlux/apps/inventoryApp/src/fakeData/index.ts | 5 +- .../handlers/connectedNetworkElementsHandler.ts | 36 - .../src/handlers/inventoryAppRootHandler.ts | 18 +- .../handlers/inventoryDeviceListActionHandler.ts | 56 + .../src/handlers/inventoryElementsHandler.ts | 4 +- .../src/handlers/inventoryTreeHandler.ts | 14 +- .../apps/inventoryApp/src/handlers/panelHandler.ts | 16 +- .../apps/inventoryApp/src/models/inventory.ts | 6 +- .../src/models/inventoryDeviceListType.ts | 25 + .../src/models/networkElementConnection.ts | 6 +- .../odlux/apps/inventoryApp/src/models/panelId.ts | 2 +- .../apps/inventoryApp/src/pluginInventory.tsx | 49 +- .../inventoryApp/src/services/inventoryService.ts | 74 +- .../apps/inventoryApp/src/views/dashboard.tsx | 139 +- .../odlux/apps/inventoryApp/src/views/detail.tsx | 15 +- .../odlux/apps/inventoryApp/src/views/treeview.tsx | 63 +- sdnr/wt/odlux/apps/inventoryApp/tsconfig.json | 2 +- sdnr/wt/odlux/apps/inventoryApp/webpack.config.js | 26 +- sdnr/wt/odlux/apps/maintenanceApp/package.json | 4 +- sdnr/wt/odlux/apps/maintenanceApp/pom.xml | 3 +- .../src/actions/maintenenceActions.ts | 34 +- .../src/assets/icons/maintenanceAppIcon.svg | 50 + .../src/components/editMaintenenceEntryDialog.tsx | 103 +- .../src/components/refreshMaintenanceEntries.tsx | 62 +- .../src/handlers/maintenanceAppRootHandler.ts | 10 +- .../src/handlers/maintenanceEntriesHandler.ts | 35 + .../src/handlers/maintenenceEntriesHandler.ts | 35 - .../src/models/maintenanceEntryType.ts | 33 + .../src/models/maintenenceEntryType.ts | 33 - .../apps/maintenanceApp/src/pluginMaintenance.tsx | 18 +- .../src/services/maintenenceService.ts | 46 +- .../apps/maintenanceApp/src/utils/timeUtils.ts | 4 +- .../maintenanceApp/src/views/maintenanceView.tsx | 246 +++ .../maintenanceApp/src/views/maintenenceView.tsx | 254 --- sdnr/wt/odlux/apps/maintenanceApp/tsconfig.json | 2 +- .../wt/odlux/apps/maintenanceApp/webpack.config.js | 10 + sdnr/wt/odlux/apps/mediatorApp/pom.xml | 3 +- .../src/assets/icons/mediatorAppIcon.svg | 49 + .../src/components/showMeditaorInfoDialog.tsx | 6 +- sdnr/wt/odlux/apps/mediatorApp/src/plugin.tsx | 14 +- .../mediatorApp/src/views/mediatorApplication.tsx | 4 +- .../src/views/mediatorServerSelection.tsx | 4 +- sdnr/wt/odlux/apps/mediatorApp/tsconfig.json | 2 +- sdnr/wt/odlux/apps/mediatorApp/webpack.config.js | 10 + sdnr/wt/odlux/apps/minimumApp/pom.xml | 3 +- .../minimumApp/src/assets/icons/minimumAppIcon.svg | 27 + .../src/handlers/minimumAppRootHandler.ts | 3 +- sdnr/wt/odlux/apps/minimumApp/src/plugin.tsx | 18 +- sdnr/wt/odlux/apps/minimumApp/tsconfig.json | 2 +- sdnr/wt/odlux/apps/performanceHistoryApp/pom.xml | 3 +- .../src/actions/deviceListActions.ts | 5 +- .../performanceHistoryApp/src/actions/ltpAction.ts | 36 +- .../src/actions/reloadAction.ts | 8 +- .../src/actions/timeChangeAction.ts | 1 + .../src/actions/toggleActions.ts | 23 +- .../src/assets/icons/performanceHistoryAppIcon.svg | 50 + .../src/components/adaptiveModulation.tsx | 285 ++-- .../src/components/chartFilter.tsx | 79 +- .../src/components/crossPolarDiscrimination.tsx | 83 +- .../src/components/ltpSelection.tsx | 153 +- .../src/components/performanceData.tsx | 80 +- .../src/components/receiveLevel.tsx | 80 +- .../src/components/signalToInterference.tsx | 83 +- .../src/components/temperature.tsx | 80 +- .../src/components/toggleContainer.tsx | 73 +- .../src/components/transmissionPower.tsx | 89 +- .../src/handlers/adaptiveModulationHandler.ts | 12 +- .../src/handlers/availableLtpsActionHandler.ts | 26 +- .../handlers/crossPolarDiscriminationHandler.ts | 12 +- .../src/handlers/deviceListActionHandler.ts | 8 +- .../src/handlers/performanceDataHandler.ts | 13 +- .../src/handlers/performanceHistoryRootHandler.ts | 122 +- .../src/handlers/receiveLevelHandler.ts | 10 +- .../src/handlers/signalToInterferenceHandler.ts | 10 +- .../src/handlers/temperatureHandler.ts | 12 +- .../src/handlers/transmissionPowerHandler.ts | 12 +- .../src/models/availableLtps.ts | 6 +- .../performanceHistoryApp/src/models/chartTypes.ts | 16 +- .../src/models/crossPolarDiscriminationDataType.ts | 2 +- .../src/models/deviceListType.ts | 2 +- .../performanceHistoryApp/src/models/panelId.ts | 2 +- .../src/models/performanceDataType.ts | 3 +- .../src/models/temperatureDataType.ts | 2 +- .../src/models/toggleDataType.ts | 4 +- .../src/models/topologyNetconf.ts | 4 +- .../src/pluginPerformance.tsx | 89 +- .../src/services/performanceHistoryService.ts | 71 +- .../performanceHistoryApp/src/utils/chartUtils.tsx | 36 +- .../performanceHistoryApp/src/utils/tableUtils.ts | 16 +- .../src/views/performanceHistoryApplication.tsx | 215 +-- .../odlux/apps/performanceHistoryApp/tsconfig.json | 2 +- .../apps/performanceHistoryApp/webpack.config.js | 10 + sdnr/wt/odlux/eslintrc.json | 54 + sdnr/wt/odlux/framework/pom.xml | 5 +- .../odlux/framework/src/actions/settingsAction.ts | 8 +- sdnr/wt/odlux/framework/src/app.tsx | 7 +- sdnr/wt/odlux/framework/src/assets/icons/About.svg | 18 + sdnr/wt/odlux/framework/src/assets/icons/Home.svg | 28 + sdnr/wt/odlux/framework/src/assets/icons/Menu.svg | 18 + sdnr/wt/odlux/framework/src/assets/icons/Tools.svg | 35 + sdnr/wt/odlux/framework/src/assets/icons/User.svg | 21 + .../framework/src/assets/icons/ht.Connect.png | Bin 0 -> 14989 bytes .../framework/src/assets/icons/ht.Connect.svg | 81 + .../framework/src/components/errorDisplay.tsx | 4 +- .../framework/src/components/icons/menuIcon.tsx | 29 + sdnr/wt/odlux/framework/src/components/logo.tsx | 4 +- .../src/components/material-table/index.tsx | 16 +- .../components/material-table/showColumnDialog.tsx | 6 +- .../src/components/material-table/utilities.ts | 9 +- .../src/components/material-ui/listItemLink.tsx | 14 +- .../framework/src/components/navigationMenu.tsx | 27 +- .../framework/src/components/routing/appFrame.tsx | 4 +- .../framework/src/components/settings/general.tsx | 4 +- .../wt/odlux/framework/src/components/titleBar.tsx | 27 +- sdnr/wt/odlux/framework/src/flux/connect.ts | 161 -- sdnr/wt/odlux/framework/src/flux/connect.tsx | 213 +++ sdnr/wt/odlux/framework/src/flux/store.ts | 16 +- .../src/handlers/authenticationHandler.ts | 2 +- sdnr/wt/odlux/framework/src/middleware/logger.ts | 7 +- .../odlux/framework/src/middleware/navigation.ts | 2 +- .../odlux/framework/src/services/applicationApi.ts | 5 +- .../framework/src/services/applicationManager.ts | 4 +- .../framework/src/services/broadcastService.ts | 146 +- sdnr/wt/odlux/framework/src/services/index.ts | 2 +- .../wt/odlux/framework/src/services/restService.ts | 54 +- .../framework/src/services/settingsService.ts | 41 - .../odlux/framework/src/services/storeService.ts | 11 + .../framework/src/services/userdataService.ts | 41 + .../odlux/framework/src/store/applicationStore.ts | 6 +- sdnr/wt/odlux/framework/src/utilities/logLevel.ts | 8 + sdnr/wt/odlux/framework/src/views/about.tsx | 78 +- sdnr/wt/odlux/framework/src/views/frame.tsx | 143 +- sdnr/wt/odlux/framework/src/views/home.tsx | 33 +- sdnr/wt/odlux/framework/src/views/login.tsx | 320 ++-- sdnr/wt/odlux/framework/src/views/settings.tsx | 45 +- .../framework/src2/main/resources/version.json | 2 + sdnr/wt/odlux/installer/pom.xml | 4 +- sdnr/wt/odlux/odlux.properties | 18 +- sdnr/wt/odlux/package.json | 14 +- sdnr/wt/odlux/pom.xml | 1 + sdnr/wt/odlux/proxy.conf.js | 106 ++ sdnr/wt/odlux/yarn.lock | 1708 +++++++++++++++----- 278 files changed, 8456 insertions(+), 6217 deletions(-) delete mode 100644 docs/guides/onap-user/mwtnLog.rst delete mode 100644 docs/guides/onap-user/mwtnTest.rst delete mode 100644 docs/guides/onap-user/pnfEventLog.rst delete mode 100644 docs/guides/onap-user/sdnr.rst create mode 100644 sdnr/wt/odlux/.eslintignore create mode 100644 sdnr/wt/odlux/apps/configurationApp/src/assets/icons/configurationAppIcon.svg delete mode 100644 sdnr/wt/odlux/apps/configurationApp/src/components/verifyer.ts create mode 100644 sdnr/wt/odlux/apps/configurationApp/src/utilities/verifyer.ts create mode 100644 sdnr/wt/odlux/apps/configurationApp/src/utilities/viewEngineHelper.ts create mode 100644 sdnr/wt/odlux/apps/configurationApp/src/yang/whenParser.ts create mode 100644 sdnr/wt/odlux/apps/connectApp/src/assets/icons/connectAppIcon.svg create mode 100644 sdnr/wt/odlux/apps/eventLogApp/src/assets/icons/eventLogAppIcon.svg create mode 100644 sdnr/wt/odlux/apps/faultApp/src/assets/icons/faultAppIcon.svg create mode 100644 sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentAlarmsDialog.tsx delete mode 100644 sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentProblemsDialog.tsx create mode 100644 sdnr/wt/odlux/apps/faultApp/src/handlers/currentAlarmsHandler.ts delete mode 100644 sdnr/wt/odlux/apps/faultApp/src/handlers/currentProblemsHandler.ts create mode 100644 sdnr/wt/odlux/apps/helpApp/src/assets/icons/helpAppIcon.svg create mode 100644 sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryDeviceListActions.ts create mode 100644 sdnr/wt/odlux/apps/inventoryApp/src/assets/icons/inventoryAppIcon.svg delete mode 100644 sdnr/wt/odlux/apps/inventoryApp/src/handlers/connectedNetworkElementsHandler.ts create mode 100644 sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryDeviceListActionHandler.ts create mode 100644 sdnr/wt/odlux/apps/inventoryApp/src/models/inventoryDeviceListType.ts create mode 100644 sdnr/wt/odlux/apps/maintenanceApp/src/assets/icons/maintenanceAppIcon.svg create mode 100644 sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenanceEntriesHandler.ts delete mode 100644 sdnr/wt/odlux/apps/maintenanceApp/src/handlers/maintenenceEntriesHandler.ts create mode 100644 sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenanceEntryType.ts delete mode 100644 sdnr/wt/odlux/apps/maintenanceApp/src/models/maintenenceEntryType.ts create mode 100644 sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenanceView.tsx delete mode 100644 sdnr/wt/odlux/apps/maintenanceApp/src/views/maintenenceView.tsx create mode 100644 sdnr/wt/odlux/apps/mediatorApp/src/assets/icons/mediatorAppIcon.svg create mode 100644 sdnr/wt/odlux/apps/minimumApp/src/assets/icons/minimumAppIcon.svg create mode 100644 sdnr/wt/odlux/apps/performanceHistoryApp/src/assets/icons/performanceHistoryAppIcon.svg create mode 100644 sdnr/wt/odlux/eslintrc.json create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/About.svg create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/Home.svg create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/Menu.svg create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/Tools.svg create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/User.svg create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png create mode 100644 sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg create mode 100644 sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx delete mode 100644 sdnr/wt/odlux/framework/src/flux/connect.ts create mode 100644 sdnr/wt/odlux/framework/src/flux/connect.tsx delete mode 100644 sdnr/wt/odlux/framework/src/services/settingsService.ts create mode 100644 sdnr/wt/odlux/framework/src/services/storeService.ts create mode 100644 sdnr/wt/odlux/framework/src/services/userdataService.ts create mode 100644 sdnr/wt/odlux/framework/src/utilities/logLevel.ts create mode 100644 sdnr/wt/odlux/proxy.conf.js diff --git a/docs/guides/onap-user/mwtnLog.rst b/docs/guides/onap-user/mwtnLog.rst deleted file mode 100644 index 3cfd61bd1..000000000 --- a/docs/guides/onap-user/mwtnLog.rst +++ /dev/null @@ -1,10 +0,0 @@ -.. contents:: - :depth: 3 -.. - -Log -=== - -The application displays (UX) application logs. SDN-R offer a common log -server, so that PNFs or other ONAP components could log there data in a -common way. diff --git a/docs/guides/onap-user/mwtnTest.rst b/docs/guides/onap-user/mwtnTest.rst deleted file mode 100644 index 1c012fc0c..000000000 --- a/docs/guides/onap-user/mwtnTest.rst +++ /dev/null @@ -1,9 +0,0 @@ -.. contents:: - :depth: 3 -.. - -Test -==== - -The view offers in a generic way data fetched from ONF-TR-532 devices -for test and debug purposes. diff --git a/docs/guides/onap-user/pnfEventLog.rst b/docs/guides/onap-user/pnfEventLog.rst deleted file mode 100644 index 036c1f94b..000000000 --- a/docs/guides/onap-user/pnfEventLog.rst +++ /dev/null @@ -1,11 +0,0 @@ -.. contents:: - :depth: 3 -.. - -Event Log -========= - -The 'EventLog' application displays application logs and messages -automatically created by the different active applications. SDN-R offers -a common log service so that PNFs or other ONAP components can log their -data and users can analyze and export the data in a common way. diff --git a/docs/guides/onap-user/sdnr.rst b/docs/guides/onap-user/sdnr.rst deleted file mode 100644 index db690f92b..000000000 --- a/docs/guides/onap-user/sdnr.rst +++ /dev/null @@ -1,22 +0,0 @@ -.. contents:: - :depth: 3 -.. - -SDN controller persona for 'Radio' (SDN-R) -========================================== - -SDN-R adds features and functionality to the OpenDaylight-based ONAP -controller 'CCSDK/SDNC'. It is built on the Common Controller Framework -to control and manage wireless resources. Wireless resources are virtual -network functions (e.g. vBBU, vEPC) or physical network functions (e.g. -microwave and millimeter wave radios, eNodeB, RRH, DAS equipment). - -| SDN-R is integrated into ONAP using DMaaP APIs. It is interfacing with - PNFs and VNFs and with other ONAP components, such as A&AI, DCAE and - SO. -| `See abbreviations `__ - -.. figure:: ./ONAP-SDN-R.png - :alt: SDN-R in ONAP - - SDN-R in ONAP diff --git a/docs/tox.ini b/docs/tox.ini index 4bbc64032..7a8b6890e 100644 --- a/docs/tox.ini +++ b/docs/tox.ini @@ -10,7 +10,7 @@ deps = -chttps://raw.githubusercontent.com/openstack/requirements/stable/yoga/upper-constraints.txt -chttps://git.onap.org/doc/plain/etc/upper-constraints.onap.txt commands = - sphinx-build -b html -n -d {envtmpdir}/doctrees ./ {toxinidir}/_build/html + sphinx-build -W -b html -n -d {envtmpdir}/doctrees ./ {toxinidir}/_build/html echo "Generated docs available in {toxinidir}/_build/html" whitelist_externals = echo @@ -21,7 +21,7 @@ whitelist_externals = basepython = python3.8 #deps = -r{toxinidir}/requirements-docs.txt commands = echo "Link Checking not enforced" -#commands = sphinx-build -b linkcheck -d {envtmpdir}/doctrees ./ {toxinidir}/_build/linkcheck +#commands = sphinx-build -b -W linkcheck -d {envtmpdir}/doctrees ./ {toxinidir}/_build/linkcheck whitelist_externals = echo [testenv:docs-spellcheck] diff --git a/sdnr/wt/odlux/.eslintignore b/sdnr/wt/odlux/.eslintignore new file mode 100644 index 000000000..e5c39402f --- /dev/null +++ b/sdnr/wt/odlux/.eslintignore @@ -0,0 +1,12 @@ +**/dist/** +**/build/** +**/node_modules/** +apps/eventApp/**/* +apps/faultApp/**/* +apps/helpApp/**/* +apps/inventoryApp/**/* +apps/lineOfSightApp/**/* +apps/maintenanceApp/**/* +apps/mediatorApp/**/* +apps/performanceHistoryApp/**/* +apps/siteManagerApp/**/* \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/apiDemo/package.json b/sdnr/wt/odlux/apps/apiDemo/package.json index f01a396e8..ff9e3c47e 100644 --- a/sdnr/wt/odlux/apps/apiDemo/package.json +++ b/sdnr/wt/odlux/apps/apiDemo/package.json @@ -23,6 +23,9 @@ "dependencies": { "@emotion/react": "^11.7.0", "@emotion/styled": "^11.6.0", + "@fortawesome/fontawesome-svg-core": "1.2.35", + "@fortawesome/free-solid-svg-icons": "5.6.3", + "@fortawesome/react-fontawesome": "0.1.14", "@mui/icons-material": "^5.2.0", "@mui/material": "^5.2.2", "@mui/styles": "^5.2.2", diff --git a/sdnr/wt/odlux/apps/apiDemo/pom.xml b/sdnr/wt/odlux/apps/apiDemo/pom.xml index b6dc53e93..97f7faa80 100644 --- a/sdnr/wt/odlux/apps/apiDemo/pom.xml +++ b/sdnr/wt/odlux/apps/apiDemo/pom.xml @@ -19,6 +19,7 @@ ~ ============LICENSE_END======================================================= ~ --> + 4.0.0 @@ -139,7 +140,7 @@ initialize - v12.13.0 + v12.22.0 v1.22.10 diff --git a/sdnr/wt/odlux/apps/apiDemo/src/handlers/apiDemoRootHandler.ts b/sdnr/wt/odlux/apps/apiDemo/src/handlers/apiDemoRootHandler.ts index 36688b3a5..128a03286 100644 --- a/sdnr/wt/odlux/apps/apiDemo/src/handlers/apiDemoRootHandler.ts +++ b/sdnr/wt/odlux/apps/apiDemo/src/handlers/apiDemoRootHandler.ts @@ -18,22 +18,23 @@ import { combineActionHandler } from '../../../../framework/src/flux/middleware'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import { moduleHandler, IModules } from './modulesHandler'; export interface IApiDemoStoreState { - modules: IModules + modules: IModules; } declare module '../../../../framework/src/store/applicationStore' { interface IApplicationStoreState { - apiDemo: IApiDemoStoreState + apiDemo: IApiDemoStoreState; } } const actionHandlers = { - modules: moduleHandler + modules: moduleHandler, }; export const apiDemoRootHandler = combineActionHandler(actionHandlers); diff --git a/sdnr/wt/odlux/apps/apiDemo/src/handlers/modulesHandler.ts b/sdnr/wt/odlux/apps/apiDemo/src/handlers/modulesHandler.ts index 8777355db..1984a2d10 100644 --- a/sdnr/wt/odlux/apps/apiDemo/src/handlers/modulesHandler.ts +++ b/sdnr/wt/odlux/apps/apiDemo/src/handlers/modulesHandler.ts @@ -20,7 +20,7 @@ import { IActionHandler } from '../../../../framework/src/flux/action'; import { ModulesRequestSuccess } from '../actions/modulesSuccess'; import { Module } from '../models/module'; -export type IModules = Module[] +export type IModules = Module[]; const modulesInit: IModules = []; diff --git a/sdnr/wt/odlux/apps/apiDemo/src/models/module.ts b/sdnr/wt/odlux/apps/apiDemo/src/models/module.ts index abf86b76e..48772a785 100644 --- a/sdnr/wt/odlux/apps/apiDemo/src/models/module.ts +++ b/sdnr/wt/odlux/apps/apiDemo/src/models/module.ts @@ -19,10 +19,10 @@ export type Module = { name: string; revision: string; namespace: string; -} +}; export type ModuleResult = { modules: { - module: Module[] - } -} \ No newline at end of file + 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 index 076eb5c70..2f70d8e2d 100644 --- a/sdnr/wt/odlux/apps/apiDemo/src/plugin.tsx +++ b/sdnr/wt/odlux/apps/apiDemo/src/plugin.tsx @@ -15,20 +15,20 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from "react"; -import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; +import React from 'react'; +import { withRouter, RouteComponentProps } from 'react-router-dom'; -import { faNewspaper } from '@fortawesome/free-solid-svg-icons'; +import { faNewspaper } from '@fortawesome/free-solid-svg-icons/faNewspaper'; import applicationManager from '../../../framework/src/services/applicationManager'; -import connect, { Connect } from '../../../framework/src/flux/connect'; +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 }; +type AppProps = RouteComponentProps & Connect & { modules: Module[]; requestModules: () => void }; const App = (props: AppProps ) => ( <> @@ -38,16 +38,16 @@ const App = (props: AppProps ) => ( ); const FinalApp = withRouter(connect((state) => ({ - modules: state.apiDemo.modules + modules: state.apiDemo.modules, }), (dispatcher => ({ - requestModules: () => { dispatcher.dispatch(new ApiAction('restconf/modules', ModulesRequestSuccess, true)) } + requestModules: () => { dispatcher.dispatch(new ApiAction('restconf/modules', ModulesRequestSuccess, true)); }, })))(App)); applicationManager.registerApplication({ - name: "apiDemo", + name: 'apiDemo', icon: faNewspaper, rootComponent: FinalApp, rootActionHandler: apiDemoRootHandler, - menuEntry: "API Demo" + menuEntry: 'API Demo', }); diff --git a/sdnr/wt/odlux/apps/apiDemo/tsconfig.json b/sdnr/wt/odlux/apps/apiDemo/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/apiDemo/tsconfig.json +++ b/sdnr/wt/odlux/apps/apiDemo/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/configurationApp/package.json b/sdnr/wt/odlux/apps/configurationApp/package.json index afd5456ec..b1d7d95d5 100644 --- a/sdnr/wt/odlux/apps/configurationApp/package.json +++ b/sdnr/wt/odlux/apps/configurationApp/package.json @@ -26,7 +26,11 @@ "@mui/icons-material": "^5.2.0", "@mui/material": "^5.2.2", "@mui/styles": "^5.2.2", - "@odlux/framework": "*" + "@odlux/framework": "*", + "@fortawesome/fontawesome-svg-core": "1.2.35", + "@fortawesome/free-solid-svg-icons": "5.6.3", + "@fortawesome/react-fontawesome": "0.1.14", + "material-ui-confirm": "3.0.2" }, "peerDependencies": { "@types/classnames": "2.2.6", diff --git a/sdnr/wt/odlux/apps/configurationApp/pom.xml b/sdnr/wt/odlux/apps/configurationApp/pom.xml index 63475625d..9703e2924 100644 --- a/sdnr/wt/odlux/apps/configurationApp/pom.xml +++ b/sdnr/wt/odlux/apps/configurationApp/pom.xml @@ -19,6 +19,7 @@ ~ ============LICENSE_END======================================================= ~ --> + 4.0.0 @@ -139,7 +140,7 @@ initialize - v12.13.0 + v12.22.0 v1.22.10 diff --git a/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts index 37583787f..52137135a 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/actions/deviceActions.ts @@ -1,15 +1,30 @@ 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 { AddErrorInfoAction } from "../../../../framework/src/actions/errorActions"; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { PushAction, ReplaceAction } from '../../../../framework/src/actions/navigationActions'; +import { AddErrorInfoAction } from '../../../../framework/src/actions/errorActions'; import { DisplayModeType, DisplaySpecification } from '../handlers/viewDescriptionHandler'; -import { restService } from "../services/restServices"; -import { YangParser } from "../yang/yangParser"; -import { Module } from "../models/yang"; -import { ViewSpecification, ViewElement, isViewElementReference, isViewElementList, isViewElementObjectOrList, isViewElementRpc, isViewElementChoise, ViewElementChoiseCase, ViewElementString } from "../models/uiModels"; +import { restService } from '../services/restServices'; +import { YangParser } from '../yang/yangParser'; +import { Module } from '../models/yang'; +import { + ViewSpecification, + ViewElement, + isViewElementReference, + isViewElementList, + ViewElementString, +} from '../models/uiModels'; + +import { + checkResponseCode, + splitVPath, + filterViewElements, + flattenViewElements, + getReferencedDataList, + resolveViewDescription, +} from '../utilities/viewEngineHelper'; export class EnableValueSelector extends Action { constructor(public listSpecification: ViewSpecification, public listData: any[], public keyProperty: string, public onValueSelected : (value: any) => void ) { @@ -30,42 +45,26 @@ export class SetSelectedValue extends Action { } export class UpdateDeviceDescription extends Action { - constructor( public nodeId: string, public modules: { [name:string]: Module}, public views: ViewSpecification[]) { + constructor( public nodeId: string, public modules: { [name:string]: Module }, public views: ViewSpecification[]) { super(); } } -export class UpdatViewDescription extends Action { - constructor (public vPath: string, public viewData: any, public displaySpecification: DisplaySpecification = { displayMode: DisplayModeType.doNotDisplay }) { +export class UpdateViewDescription extends Action { + constructor(public vPath: string, public viewData: any, public displaySpecification: DisplaySpecification = { displayMode: DisplayModeType.doNotDisplay }) { super(); } } -export class UpdatOutputData extends Action { - constructor (public outputData: any) { +export class UpdateOutputData extends Action { + constructor(public outputData: any) { super(); } } -type HttpResult = { - status: number; - message?: string | undefined; - data: { - [key: string]: any; - } | null | undefined; -}; - -const checkResponseCode = (restResult: HttpResult) =>{ - - //403 gets handled by the framework from now on - - return restResult.status !== 403 && ( restResult.status < 200 || restResult.status > 299); +export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatch: Dispatch, _getState: () => IApplicationStoreState ) => { -} - -export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState ) => { - - dispatch(new UpdateDeviceDescription("", {}, [])); + dispatch(new UpdateDeviceDescription('', {}, [])); dispatch(new SetCollectingSelectionData(true)); const { availableCapabilities, unavailableCapabilities, importOnlyModules } = await restService.getCapabilitiesByMountId(nodeId); @@ -73,16 +72,24 @@ export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatc if (!availableCapabilities || availableCapabilities.length <= 0) { dispatch(new SetCollectingSelectionData(false)); dispatch(new UpdateDeviceDescription(nodeId, {}, [])); - dispatch(new UpdatViewDescription("", [], { + dispatch(new UpdateViewDescription('', [], { displayMode: DisplayModeType.displayAsMessage, - renderMessage: `NetworkElement : "${nodeId}" has no capabilities.` + renderMessage: `NetworkElement : "${nodeId}" has no capabilities.`, })); throw new Error(`NetworkElement : [${nodeId}] has no capabilities.`); } - const parser = new YangParser(unavailableCapabilities || undefined, importOnlyModules || undefined, nodeId); + const parser = new YangParser( + nodeId, + availableCapabilities.reduce((acc, cur) => { + acc[cur.capability] = cur.version; + return acc; + }, {} as { [key: string]: string }), + unavailableCapabilities || undefined, + importOnlyModules || undefined, + ); - for (let i = 0; i < availableCapabilities.length; ++i){ + for (let i = 0; i < availableCapabilities.length; ++i) { const capRaw = availableCapabilities[i]; try { await parser.addCapability(capRaw.capability, capRaw.version); @@ -95,184 +102,28 @@ export const updateNodeIdAsyncActionCreator = (nodeId: string) => async (dispatc dispatch(new SetCollectingSelectionData(false)); - if (process.env.NODE_ENV === "development" ) { - console.log(parser, parser.modules, parser.views); + if (process.env.NODE_ENV === 'development' ) { + console.log(parser, 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 - const defaultNS = pathParts[0][0]; - let referencedModule = modules[defaultNS]; - - 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 || checkResponseCode(restResult)) { - 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[`${defaultNS}:${dataMember!}`]; - if (dataRaw === undefined) { - 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 || checkResponseCode(restResult)) { - 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[`${defaultNS}:${dataMember!}`]; - if (dataRaw === undefined) { - 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, - }; - } +export const postProcessDisplaySpecificationActionCreator = (vPath: string, viewData: any, displaySpecification: DisplaySpecification) => async (dispatch: Dispatch, _getState: () => IApplicationStoreState) => { + + if (displaySpecification.displayMode === DisplayModeType.displayAsObject) { + displaySpecification = { + ...displaySpecification, + viewSpecification: await filterViewElements(vPath, viewData, displaySpecification.viewSpecification), + }; } - return null; -} - -const resolveViewDescription = (defaultNS: string | null, vPath: string, view: ViewSpecification): ViewSpecification =>{ - - // check if-feature | when | and resolve all references. - view = { ...view }; - view.elements = Object.keys(view.elements).reduce<{ [name: string]: ViewElement }>((acc, cur) => { - const resolveHistory : ViewElement[] = []; - let elm = view.elements[cur]; - const key = defaultNS && cur.replace(new RegExp(`^${defaultNS}:`, "i"),"") || cur; - while (isViewElementReference(elm)) { - const result = (elm.ref(vPath)); - if (result) { - const [referencedElement, referencedPath] = result; - if (resolveHistory.some(hist => hist === referencedElement)) { - console.error(`Circle reference found at: ${vPath}`, resolveHistory); - break; - } - elm = referencedElement; - vPath = referencedPath; - resolveHistory.push(elm); - } - } - - acc[key] = { ...elm, id: key }; - - return acc; - }, {}); - return view; -} - -const flatenViewElements = (defaultNS: string | null, parentPath: string, elements: { [name: string]: ViewElement }, views: ViewSpecification[], currentPath: string ): { [name: string]: ViewElement } => { - if (!elements) return {}; - return Object.keys(elements).reduce<{ [name: string]: ViewElement }>((acc, cur) => { - const elm = elements[cur]; - // remove the detault namespace, and only the default namespace, sine it seems that this is also not in the restconf response - const elmKey = defaultNS && elm.id.replace(new RegExp(`^${defaultNS}:`, "i"), "") || elm.id; - const key = parentPath ? `${parentPath}.${elmKey}` : elmKey; - - if (isViewElementRpc(elm)) { - console.warn(`Flaten of RFC not supported ! [${currentPath}][${elm.label}]`); - return acc; - } else if (isViewElementObjectOrList(elm)) { - const view = views[+elm.viewId]; - const inner = view && flatenViewElements(defaultNS, key, view.elements, views, `${currentPath}/${view.name}`); - inner && Object.keys(inner).forEach(k => (acc[k] = inner[k])); - } else if (isViewElementChoise(elm)) { - acc[key] = { - ...elm, - id: key, - cases: Object.keys(elm.cases).reduce<{ [name: string]: ViewElementChoiseCase }>((accCases, curCases) => { - const caseElement = elm.cases[curCases]; - accCases[curCases] = { - ...caseElement, - // Hint: do not use key it contains elmKey, which shell be omitted for cases. - elements: flatenViewElements(defaultNS, /*key*/ parentPath, caseElement.elements, views, `${currentPath}/${elm.label}`) - }; - return accCases; - }, {}), - }; - } else { - acc[key] = { - ...elm, - id: key, - }; - } - return acc; - }, {}); + dispatch(new UpdateViewDescription(vPath, viewData, displaySpecification)); }; 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(); + const { configuration: { deviceDescription: { nodeId, modules, views } } } = getState(); let dataPath = `/rests/data/network-topology:network-topology/topology=topology-netconf/node=${nodeId}/yang-ext:mount`; let inputViewSpecification: ViewSpecification | undefined = undefined; @@ -291,26 +142,26 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: try { for (let ind = 0; ind < pathParts.length; ++ind) { const [property, key] = pathParts[ind]; - const namespaceInd = property && property.indexOf(":") || -1; + const namespaceInd = property && property.indexOf(':') || -1; const namespace: string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS; - if (ind === 0) { defaultNS = namespace }; + if (ind === 0) { defaultNS = namespace; } viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`]; - if (!viewElement) throw Error("Property [" + property + "] does not exist."); + 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) { + 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]; + 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 || "" + acc[elm.id] = elm.default || ''; } return acc; }, {}); @@ -319,13 +170,13 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: const ds: DisplaySpecification = { displayMode: DisplayModeType.displayAsObject, viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification), - keyProperty: isViewElementList(viewElement!) && viewElement.key || undefined + keyProperty: isViewElementList(viewElement!) && viewElement.key || undefined, }; // update display specification - return dispatch(new UpdatViewDescription(vPath, data, ds)); + return dispatch(postProcessDisplaySpecificationActionCreator(vPath, data, ds)); } - if (viewElement && isViewElementList(viewElement) && viewSpecification.parentView === "0") { + 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]; @@ -338,35 +189,35 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: 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")}]`))); + window.setTimeout(() => dispatch(new PushAction(`${vPath}[${refKey.replace(/\//ig, '%2F')}]`))); })); } else { - // Found a list at root level of a module w/o a refenrece key. + // Found a list at root level of a module w/o a reference key. dataPath += `?content=config&fields=${encodeURIComponent(viewElement.id)}(${encodeURIComponent(viewElement.key || '')})`; const restResult = (await restService.getConfigData(dataPath)); - if (restResult && restResult.status === 200 && restResult.data && restResult.data[viewElement.id] ){ - // spoof the not existing view here - const refData = restResult.data[viewElement.id]; - const refView : ViewSpecification = { - id: "-1", - canEdit: false, + if (restResult && restResult.status === 200 && restResult.data && restResult.data[viewElement.id] ) { + // spoof the not existing view here + const refData = restResult.data[viewElement.id]; + const refView : ViewSpecification = { + id: '-1', + canEdit: false, + config: false, + language: 'en-US', + elements: { + [viewElement.key!] : { + uiType: 'string', config: false, - language: "en-US", - elements: { - [viewElement.key!] : { - uiType: "string", - config: false, - id: viewElement.key, - label: viewElement.key, - isList: true, - } as ViewElementString - } - }; - dispatch(new EnableValueSelector(refView, refData, viewElement.key!, (refKey) => { - window.setTimeout(() => dispatch(new PushAction(`${vPath}[${refKey.replace(/\//ig, "%2F")}]`))); - })); + id: viewElement.key, + label: viewElement.key, + isList: true, + } as ViewElementString, + }, + }; + dispatch(new EnableValueSelector(refView, refData, viewElement.key!, (refKey) => { + window.setTimeout(() => dispatch(new PushAction(`${vPath}[${refKey.replace(/\//ig, '%2F')}]`))); + })); } else { - throw new Error("Found a list at root level of a module and could not determine the keys."); + throw new Error('Found a list at root level of a module and could not determine the keys.'); } dispatch(new SetCollectingSelectionData(false)); } @@ -374,31 +225,30 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: } extractList = true; } else { - // normal case + // normal case & replaces unicode %2C if present + dataPath += `/${property}${key ? `=${key.replace(/\%2C/g, ',').replace(/\//ig, '%2F')}` : ''}`; + // in case of the root element the required namespace will be added later, // while extracting the data - - dataPath += `/${property}${key ? `=${key.replace(/\%2C/g, ",").replace(/\//ig, "%2F")}` : ""}`; - dataMember = namespace === defaultNS ? viewElement.label : `${namespace}:${viewElement.label}`; extractList = false; } - if (viewElement && "viewId" in viewElement) { + if (viewElement && 'viewId' in viewElement) { viewSpecification = views[+viewElement.viewId]; - } else if (viewElement.uiType === "rpc") { + } else if (viewElement.uiType === 'rpc') { viewSpecification = views[+(viewElement.inputViewId || 0)]; // create new instance & flaten inputViewSpecification = viewElement.inputViewId != null && { ...views[+(viewElement.inputViewId || 0)], - elements: flatenViewElements(defaultNS, "", views[+(viewElement.inputViewId || 0)].elements, views, viewElement.label), + elements: flattenViewElements(defaultNS, '', views[+(viewElement.inputViewId || 0)].elements, views, viewElement.label), } || undefined; outputViewSpecification = viewElement.outputViewId != null && { ...views[+(viewElement.outputViewId || 0)], - elements: flatenViewElements(defaultNS, "", views[+(viewElement.outputViewId || 0)].elements, views, viewElement.label), + elements: flattenViewElements(defaultNS, '', views[+(viewElement.outputViewId || 0)].elements, views, viewElement.label), } || undefined; } @@ -406,7 +256,7 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: let data: any = {}; // do not get any data from netconf if there is no view specified || this is the root element [0] || this is an rpc - if (viewSpecification && !(viewSpecification.id === "0" || viewElement!.uiType === "rpc")) { + if (viewSpecification && !(viewSpecification.id === '0' || viewElement!.uiType === 'rpc')) { const restResult = (await restService.getConfigData(dataPath)); if (!restResult.data) { // special case: if this is a list without any response @@ -418,21 +268,21 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: const ds: DisplaySpecification = { displayMode: extractList ? DisplayModeType.displayAsList : DisplayModeType.displayAsObject, viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification), - keyProperty: viewElement.key + keyProperty: viewElement.key, }; // update display specification - return dispatch(new UpdatViewDescription(vPath, [], ds)); + return dispatch(postProcessDisplaySpecificationActionCreator(vPath, [], ds)); } throw new Error(`Did not get response from Server. Status: [${restResult.status}]`); } else if (checkResponseCode(restResult)) { - const message = restResult.data.errors && restResult.data.errors.error && restResult.data.errors.error[0] && restResult.data.errors.error[0]["error-message"] || ""; + 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 { - // https://tools.ietf.org/html/rfc7951#section-4 the root element may countain a namesapce or not ! + // https://tools.ietf.org/html/rfc7951#section-4 the root element may contain a namespace or not ! data = restResult.data[`${defaultNS}:${dataMember!}`]; if (data === undefined) { - data = restResult.data[dataMember!]; // extract dataMember w/o namespace + data = restResult.data[dataMember!]; // extract dataMember w/o namespace } } @@ -446,19 +296,21 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: ? data[viewElement!.id] || data[viewElement!.label] || [] // if the list is empty, it does not exist : data; - } else if (viewElement! && viewElement!.uiType === "rpc") { + } else if (viewElement! && viewElement!.uiType === 'rpc') { // set data to defaults data = {}; - inputViewSpecification && Object.keys(inputViewSpecification.elements).forEach(key => { - const elm = inputViewSpecification && inputViewSpecification.elements[key]; - if (elm && elm.default != undefined) { - data[elm.id] = elm.default; - } - }); + if (inputViewSpecification) { + Object.keys(inputViewSpecification.elements).forEach(key => { + const elm = inputViewSpecification && inputViewSpecification.elements[key]; + if (elm && elm.default != undefined) { + data[elm.id] = elm.default; + } + }); + } } // create display specification - const ds: DisplaySpecification = viewElement! && viewElement!.uiType === "rpc" + const ds: DisplaySpecification = viewElement! && viewElement!.uiType === 'rpc' ? { dataPath, displayMode: DisplayModeType.displayAsRPC, @@ -470,16 +322,18 @@ export const updateViewActionAsyncCreator = (vPath: string) => async (dispatch: displayMode: extractList ? DisplayModeType.displayAsList : DisplayModeType.displayAsObject, viewSpecification: resolveViewDescription(defaultNS, vPath, viewSpecification), keyProperty: isViewElementList(viewElement!) && viewElement.key || undefined, - apidocPath: isViewElementList(viewElement!) && `/apidoc/explorer/index.html?urls.primaryName=$$$standard$$$#/mounted%20${nodeId}%20${viewElement!.module || 'MODULE_NOT_DEFINED'}/$$$action$$$_${dataPath.replace(/^\//,'').replace(/[\/=\-\:]/g,'_')}_${viewElement! != null ? `${viewElement.id.replace(/[\/=\-\:]/g,'_')}_` : '' }` || undefined, + + // eslint-disable-next-line max-len + apidocPath: isViewElementList(viewElement!) && `/apidoc/explorer/index.html?urls.primaryName=$$$standard$$$#/mounted%20${nodeId}%20${viewElement!.module || 'MODULE_NOT_DEFINED'}/$$$action$$$_${dataPath.replace(/^\//, '').replace(/[\/=\-\:]/g, '_')}_${viewElement! != null ? `${viewElement.id.replace(/[\/=\-\:]/g, '_')}_` : '' }` || undefined, }; // update display specification - return dispatch(new UpdatViewDescription(vPath, data, ds)); - // 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 + return dispatch(postProcessDisplaySpecificationActionCreator(vPath, data, ds)); + // https://server.com/#/configuration/Sim12600/core-model:network-element/ltp[LTP-MWPS-TTP-01] + // https://server.com/#/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 AddErrorInfoAction({ title: 'Problem', message: error.message || `Could not process ${dataPath}` })); dispatch(new SetCollectingSelectionData(false)); } finally { return; @@ -503,63 +357,63 @@ export const updateDataActionAsyncCreator = (vPath: string, data: any) => async try { for (let ind = 0; ind < pathParts.length; ++ind) { let [property, key] = pathParts[ind]; - const namespaceInd = property && property.indexOf(":") || -1; + const namespaceInd = property && property.indexOf(':') || -1; const namespace: string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS; - if (ind === 0) { defaultNS = namespace }; + if (ind === 0) { defaultNS = namespace; } viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`]; - if (!viewElement) throw Error("Property [" + property + "] does not exist."); + 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 (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) { + throw new Error('No key for list [' + property + ']'); + } else if (vPath.endsWith('[]') && pathParts.length - 1 === ind) { // handle new element with any number of arguments - let keyList = viewElement.key?.split(" "); - let dataPathParam = keyList?.map(id => data[id]).join(","); - key = viewElement.key && String(dataPathParam) || ""; + let keyList = viewElement.key?.split(' '); + let dataPathParam = keyList?.map(id => data[id]).join(','); + key = viewElement.key && String(dataPathParam) || ''; isNew = key; if (!key) { dispatch(new SetCollectingSelectionData(false)); - throw new Error("No value for key [" + viewElement.key + "] in list [" + property + "]"); + throw new Error('No value for key [' + viewElement.key + '] in list [' + property + ']'); } } } - dataPath += `/${property}${key ? `=${key.replace(/\//ig, "%2F")}` : ""}`; + dataPath += `/${property}${key ? `=${key.replace(/\//ig, '%2F')}` : ''}`; dataMember = viewElement.label; embedList = false; - if (viewElement && "viewId" in viewElement) { + if (viewElement && 'viewId' in viewElement) { viewSpecification = views[+viewElement.viewId]; } } // remove read-only elements - const removeReadOnlyElements = (viewSpecification: ViewSpecification, isList: boolean, data: any) => { + const removeReadOnlyElements = (pViewSpecification: ViewSpecification, isList: boolean, pData: any) => { if (isList) { - return data.map((elm : any) => removeReadOnlyElements(viewSpecification, false, elm)); + return pData.map((elm : any) => removeReadOnlyElements(pViewSpecification, false, elm)); } else { - return Object.keys(data).reduce<{[key: string]: any}>((acc, cur)=>{ - const [nsOrName, name] = cur.split(':',1); - const element = viewSpecification.elements[cur] || viewSpecification.elements[nsOrName] || viewSpecification.elements[name]; - if (!element && process.env.NODE_ENV === "development" ) { - throw new Error("removeReadOnlyElements: Could not determine elment for data."); + return Object.keys(pData).reduce<{ [key: string]: any }>((acc, cur)=>{ + const [nsOrName, name] = cur.split(':', 1); + const element = pViewSpecification.elements[cur] || pViewSpecification.elements[nsOrName] || pViewSpecification.elements[name]; + if (!element && process.env.NODE_ENV === 'development' ) { + throw new Error('removeReadOnlyElements: Could not determine elment for data.'); } if (element && element.config) { - if (element.uiType==="object") { + if (element.uiType === 'object') { const view = views[+element.viewId]; if (!view) { - throw new Error("removeReadOnlyElements: Internal Error could not determine viewId: "+element.viewId); + throw new Error('removeReadOnlyElements: Internal Error could not determine viewId: ' + element.viewId); } - acc[cur] = removeReadOnlyElements(view, element.isList != null && element.isList, data[cur]); + acc[cur] = removeReadOnlyElements(view, element.isList != null && element.isList, pData[cur]); } else { - acc[cur] = data[cur]; + acc[cur] = pData[cur]; } } return acc; @@ -580,10 +434,10 @@ export const updateDataActionAsyncCreator = (vPath: string, data: any) => async : data; // do not extract root member (0) - if (viewSpecification && viewSpecification.id !== "0") { + if (viewSpecification && viewSpecification.id !== '0') { const updateResult = await restService.setConfigData(dataPath, { [`${currentNS}:${dataMember!}`]: data }); // addDataMember using currentNS if (checkResponseCode(updateResult)) { - const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]["error-message"] || ""; + 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 || ''}`); } } @@ -600,10 +454,10 @@ export const updateDataActionAsyncCreator = (vPath: string, data: any) => async }; // update display specification - return dispatch(new UpdatViewDescription(vPath, data, ds)); + return dispatch(new UpdateViewDescription(vPath, data, ds)); } catch (error) { history.back(); - dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not change ${dataPath}` })); + dispatch(new AddErrorInfoAction({ title: 'Problem', message: error.message || `Could not change ${dataPath}` })); } finally { dispatch(new SetCollectingSelectionData(false)); @@ -619,57 +473,53 @@ export const removeElementActionAsyncCreator = (vPath: string) => async (dispatc let viewElement: ViewElement; 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 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] || viewSpecification.elements[`${namespace}:${property}`]; - if (!viewElement) throw Error("Property [" + property + "] does not exist."); + if (!viewElement) throw Error('Property [' + property + '] does not exist.'); if (isViewElementList(viewElement) && !key) { - 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 (viewElement && viewElement.isList && viewSpecification.parentView === '0') { + throw new Error('Found a list at root level of a module w/o a reference 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) { + throw new Error('No key for list [' + property + ']'); + } else if (vPath.endsWith('[]') && pathParts.length - 1 === ind) { // remove the whole table } } - dataPath += `/${property}${key ? `=${key.replace(/\//ig, "%2F")}` : ""}`; + dataPath += `/${property}${key ? `=${key.replace(/\//ig, '%2F')}` : ''}`; - if (viewElement && "viewId" in viewElement) { + if (viewElement && 'viewId' in viewElement) { viewSpecification = views[+viewElement.viewId]; - } else if (viewElement.uiType === "rpc") { + } else if (viewElement.uiType === 'rpc') { viewSpecification = views[+(viewElement.inputViewId || 0)]; } } const updateResult = await restService.removeConfigElement(dataPath); if (checkResponseCode(updateResult)) { - const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]["error-message"] || ""; + 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 || ''}`); } } catch (error) { - dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not remove ${dataPath}` })); + dispatch(new AddErrorInfoAction({ title: 'Problem', message: error.message || `Could not remove ${dataPath}` })); } finally { dispatch(new SetCollectingSelectionData(false)); } - - }; export const executeRpcActionAsyncCreator = (vPath: string, data: any) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => { const pathParts = splitVPath(vPath, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key - const { configuration: { deviceDescription: { nodeId, views }, viewDescription: oldViewDescription } } = getState(); + const { configuration: { deviceDescription: { nodeId, views } } } = getState(); let dataPath = `/rests/operations/network-topology:network-topology/topology=topology-netconf/node=${nodeId}/yang-ext:mount`; let viewSpecification: ViewSpecification = views[0]; let viewElement: ViewElement; @@ -684,17 +534,17 @@ export const executeRpcActionAsyncCreator = (vPath: string, data: any) => async try { for (let ind = 0; ind < pathParts.length; ++ind) { let [property, key] = pathParts[ind]; - const namespaceInd = property && property.indexOf(":") || -1; + const namespaceInd = property && property.indexOf(':') || -1; const namespace: string | null = namespaceInd > -1 ? (currentNS = property.slice(0, namespaceInd)) : currentNS; - if (ind === 0) { defaultNS = namespace }; + if (ind === 0) { defaultNS = namespace; } viewElement = viewSpecification.elements[property] || viewSpecification.elements[`${namespace}:${property}`]; - if (!viewElement) throw Error("Property [" + property + "] does not exist."); + 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."); + // throw new Error("Found a list at root level of a module w/o a reference key."); // } // if (pathParts.length - 1 > ind) { // dispatch(new SetCollectingSelectionData(false)); @@ -710,28 +560,28 @@ export const executeRpcActionAsyncCreator = (vPath: string, data: any) => async // } } - dataPath += `/${property}${key ? `=${key.replace(/\//ig, "%2F")}` : ""}`; + dataPath += `/${property}${key ? `=${key.replace(/\//ig, '%2F')}` : ''}`; dataMember = viewElement.label; embedList = false; - if (viewElement && "viewId" in viewElement) { + if (viewElement && 'viewId' in viewElement) { viewSpecification = views[+viewElement.viewId]; - } else if (viewElement.uiType === "rpc") { + } else if (viewElement.uiType === 'rpc') { viewSpecification = views[+(viewElement.inputViewId || 0)]; } } // re-inflate formerly flatten rpc data data = data && Object.keys(data).reduce < { [name: string ]: any }>((acc, cur) => { - const pathParts = cur.split("."); + const innerPathParts = cur.split('.'); let pos = 0; const updatePath = (obj: any, key: string) => { - obj[key] = (pos >= pathParts.length) + obj[key] = (pos >= innerPathParts.length) ? data[cur] - : updatePath(obj[key] || {}, pathParts[pos++]); + : updatePath(obj[key] || {}, innerPathParts[pos++]); return obj; - } - updatePath(acc, pathParts[pos++]); + }; + updatePath(acc, innerPathParts[pos++]); return acc; }, {}) || null; @@ -746,22 +596,22 @@ export const executeRpcActionAsyncCreator = (vPath: string, data: any) => async : data; // do not post root member (0) - if ((viewSpecification && viewSpecification.id !== "0") || (dataMember! && !data)) { + if ((viewSpecification && viewSpecification.id !== '0') || (dataMember! && !data)) { const updateResult = await restService.executeRpc(dataPath, { [`${defaultNS}:input`]: data || {} }); if (checkResponseCode(updateResult)) { - const message = updateResult.data && updateResult.data.errors && updateResult.data.errors.error && updateResult.data.errors.error[0] && updateResult.data.errors.error[0]["error-message"] || ""; + 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 || ''}`); } - dispatch(new UpdatOutputData(updateResult.data)); + dispatch(new UpdateOutputData(updateResult.data)); } else { - throw new Error(`There is NO RPC specified.`); + throw new Error('There is NO RPC specified.'); } // // update display specification // return } catch (error) { - dispatch(new AddErrorInfoAction({ title: "Problem", message: error.message || `Could not change ${dataPath}` })); + dispatch(new AddErrorInfoAction({ title: 'Problem', message: error.message || `Could not change ${dataPath}` })); } finally { dispatch(new SetCollectingSelectionData(false)); diff --git a/sdnr/wt/odlux/apps/configurationApp/src/assets/icons/configurationAppIcon.svg b/sdnr/wt/odlux/apps/configurationApp/src/assets/icons/configurationAppIcon.svg new file mode 100644 index 000000000..1b74cc479 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/assets/icons/configurationAppIcon.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts b/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts index 26c3944c9..7187c0a4e 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/baseProps.ts @@ -16,13 +16,13 @@ * ============LICENSE_END========================================================================== */ -import { ViewElement } from "../models/uiModels"; +import { ViewElement } from '../models/uiModels'; export type BaseProps = { - value: ViewElement, - inputValue: TValue, - readOnly: boolean, - disabled: boolean, - onChange(newValue: TValue): void; - isKey?: boolean + value: ViewElement; + inputValue: TValue; + readOnly: boolean; + disabled: boolean; + onChange(newValue: TValue): void; + isKey?: boolean; }; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/ifWhenTextInput.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/ifWhenTextInput.tsx index 8ce3106a6..b176e5db5 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/ifWhenTextInput.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/ifWhenTextInput.tsx @@ -16,82 +16,86 @@ * ============LICENSE_END========================================================================== */ -import { ViewElementBase } from "models/uiModels"; -import { - TextField, - InputAdornment, - Input, - Tooltip, - Divider, - IconButton, - InputBase, - Paper, - Theme, - FormControl, - InputLabel, - FormHelperText, -} from "@mui/material"; +import React from 'react'; +import InputAdornment from '@mui/material/InputAdornment'; +import Input, { InputProps } from '@mui/material/Input'; +import Tooltip from '@mui/material/Tooltip'; +import FormControl from '@mui/material/FormControl'; +import InputLabel from '@mui/material/InputLabel'; +import FormHelperText from '@mui/material/FormHelperText'; + import makeStyles from '@mui/styles/makeStyles'; import createStyles from '@mui/styles/createStyles'; -import * as React from 'react'; -import { faAdjust } from "@fortawesome/free-solid-svg-icons"; -import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; -import { InputProps } from "@mui/material/Input"; -const useStyles = makeStyles((theme: Theme) => +import { faAdjust } from '@fortawesome/free-solid-svg-icons/faAdjust'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; + +import { ViewElementBase } from '../models/uiModels'; + +const useStyles = makeStyles(() => createStyles({ iconDark: { - color: '#ff8800' + color: '#ff8800', }, iconLight: { - color: 'orange' + color: 'orange', }, padding: { paddingLeft: 10, - paddingRight: 10 + paddingRight: 10, }, }), ); -type IfwhenProps = InputProps & { +type IfWhenProps = InputProps & { label: string; element: ViewElementBase; helperText: string; error: boolean; - onChangeTooltipVisuability(value: boolean): void; + onChangeTooltipVisibility(value: boolean): void; }; -export const IfWhenTextInput = (props: IfwhenProps) => { +export const IfWhenTextInput = (props: IfWhenProps) => { - const { element, onChangeTooltipVisuability: toogleTooltip, id, label, helperText: errorText, error, style, ...otherProps } = props; + const { element, id, label, helperText: errorText, error, style, ...otherProps } = props; const classes = useStyles(); - const ifFeature = element.ifFeature ? ( - props.onChangeTooltipVisuability(false)} onMouseOut={e => props.onChangeTooltipVisuability(true)} title={element.ifFeature}> + props.onChangeTooltipVisibility(false)} + onMouseOut={() => props.onChangeTooltipVisibility(true)} + > - ) + ) : null; const whenFeature = element.when ? ( - props.onChangeTooltipVisuability(false)} onMouseOut={() => props.onChangeTooltipVisuability(true)} title={element.when}> + props.onChangeTooltipVisibility(false)} + onMouseOut={() => props.onChangeTooltipVisibility(true)} + > - ) + ) : null; return ( {label} - {ifFeature}{whenFeature}} {...otherProps} /> + {ifFeature}{whenFeature}} {...otherProps} /> {errorText} ); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementBoolean.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementBoolean.tsx index 81c9d6dcd..56fb93cea 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementBoolean.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementBoolean.tsx @@ -16,43 +16,48 @@ * ============LICENSE_END========================================================================== */ -import * as React from "react" -import { MenuItem, FormHelperText, Select, FormControl, InputLabel } from "@mui/material"; +import React from 'react'; -import { ViewElementBoolean } from "../models/uiModels"; -import { BaseProps } from "./baseProps"; +import MenuItem from '@mui/material/MenuItem'; +import FormHelperText from '@mui/material/FormHelperText'; +import Select from '@mui/material/Select'; +import FormControl from '@mui/material/FormControl'; +import InputLabel from '@mui/material/InputLabel'; + +import { ViewElementBoolean } from '../models/uiModels'; +import { BaseProps } from './baseProps'; type BooleanInputProps = BaseProps; export const UiElementBoolean = (props: BooleanInputProps) => { - const element = props.value as ViewElementBoolean; + const element = props.value as ViewElementBoolean; - const value = String(props.inputValue).toLowerCase(); - const mandetoryError = element.mandatory && value !== 'true' && value !== 'false'; + const value = String(props.inputValue).toLowerCase(); + const mandatoryError = element.mandatory && value !== 'true' && value !== 'false'; - return (!props.readOnly || element.id != null - ? ( + return (!props.readOnly || element.id != null + ? ( {element.label} - {mandetoryError ? "Value is mandetory" : ""} + {mandatoryError ? 'Value is mandatory' : ''} ) - : null - ); -} \ No newline at end of file + : null + ); +}; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementLeafList.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementLeafList.tsx index 5937ed7b3..669ddff63 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementLeafList.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementLeafList.tsx @@ -16,19 +16,23 @@ * ============LICENSE_END========================================================================== */ -import * as React from "react" -import { FormControl, InputLabel, Paper, Chip, FormHelperText, Dialog, DialogTitle, DialogContentText, DialogActions, Button, DialogContent } from "@mui/material"; +import React from 'react'; +import FormControl from '@mui/material/FormControl'; +import InputLabel from '@mui/material/InputLabel'; +import Chip from '@mui/material/Chip'; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogActions from '@mui/material/DialogActions'; +import Button from '@mui/material/Button'; + import makeStyles from '@mui/styles/makeStyles'; import AddIcon from '@mui/icons-material/Add'; import { Theme } from '@mui/material/styles'; -import { ViewElement } from "../models/uiModels"; - -import { BaseProps } from "./baseProps"; +import { ViewElement } from '../models/uiModels'; -type LeafListProps = BaseProps & { - getEditorForViewElement: (uiElement: ViewElement) => (null | React.ComponentType>) -}; +import { BaseProps } from './baseProps'; const useStyles = makeStyles((theme: Theme) => { const light = theme.palette.mode === 'light'; @@ -50,93 +54,93 @@ const useStyles = makeStyles((theme: Theme) => { margin: theme.spacing(0.5), }, underline: { - '&:after': { - borderBottom: `2px solid ${theme.palette.primary.main}`, - left: 0, - bottom: 0, - // Doing the other way around crash on IE 11 "''" https://github.com/cssinjs/jss/issues/242 - content: '""', - position: 'absolute', - right: 0, - transform: 'scaleX(0)', - transition: theme.transitions.create('transform', { - duration: theme.transitions.duration.shorter, - easing: theme.transitions.easing.easeOut, - }), - pointerEvents: 'none', // Transparent to the hover style. - }, - '&.Mui-focused:after': { - transform: 'scaleX(1)', - }, - '&.Mui-error:after': { - borderBottomColor: theme.palette.error.main, - transform: 'scaleX(1)', // error is always underlined in red - }, - '&:before': { + '&:after': { + borderBottom: `2px solid ${theme.palette.primary.main}`, + left: 0, + bottom: 0, + // Doing the other way around crash on IE 11 "''" https://github.com/cssinjs/jss/issues/242 + content: '""', + position: 'absolute', + right: 0, + transform: 'scaleX(0)', + transition: theme.transitions.create('transform', { + duration: theme.transitions.duration.shorter, + easing: theme.transitions.easing.easeOut, + }), + pointerEvents: 'none', // Transparent to the hover style. + }, + '&.Mui-focused:after': { + transform: 'scaleX(1)', + }, + '&.Mui-error:after': { + borderBottomColor: theme.palette.error.main, + transform: 'scaleX(1)', // error is always underlined in red + }, + '&:before': { + borderBottom: `1px solid ${bottomLineColor}`, + left: 0, + bottom: 0, + // Doing the other way around crash on IE 11 "''" https://github.com/cssinjs/jss/issues/242 + content: '"\\00a0"', + position: 'absolute', + right: 0, + transition: theme.transitions.create('border-bottom-color', { + duration: theme.transitions.duration.shorter, + }), + pointerEvents: 'none', // Transparent to the hover style. + }, + '&:hover:not($disabled):before': { + borderBottom: `2px solid ${theme.palette.text.primary}`, + // Reset on touch devices, it doesn't add specificity + // eslint-disable-next-line @typescript-eslint/naming-convention + '@media (hover: none)': { borderBottom: `1px solid ${bottomLineColor}`, - left: 0, - bottom: 0, - // Doing the other way around crash on IE 11 "''" https://github.com/cssinjs/jss/issues/242 - content: '"\\00a0"', - position: 'absolute', - right: 0, - transition: theme.transitions.create('border-bottom-color', { - duration: theme.transitions.duration.shorter, - }), - pointerEvents: 'none', // Transparent to the hover style. - }, - '&:hover:not($disabled):before': { - borderBottom: `2px solid ${theme.palette.text.primary}`, - // Reset on touch devices, it doesn't add specificity - '@media (hover: none)': { - borderBottom: `1px solid ${bottomLineColor}`, - }, - }, - '&.Mui-disabled:before': { - borderBottomStyle: 'dotted', }, }, - }) + '&.Mui-disabled:before': { + borderBottomStyle: 'dotted', + }, + }, + }); }); +type LeafListProps = BaseProps & { + getEditorForViewElement: (uiElement: ViewElement) => (null | React.ComponentType>); +}; + export const UiElementLeafList = (props: LeafListProps) => { const { value: element, inputValue, onChange } = props; const classes = useStyles(); const [open, setOpen] = React.useState(false); - const [editorValue, setEditorValue] = React.useState(""); + const [editorValue, setEditorValue] = React.useState(''); const [editorValueIndex, setEditorValueIndex] = React.useState(-1); - - const handleClickOpen = () => { - setOpen(true); - }; - const handleClose = () => { setOpen(false); }; const onApplyButton = () => { - if (editorValue != null && editorValue != "" && editorValueIndex < 0) { - props.onChange([ - ...inputValue, - editorValue, - ]); - } else if (editorValue != null && editorValue != "") { - props.onChange([ - ...inputValue.slice(0, editorValueIndex), - editorValue, - ...inputValue.slice(editorValueIndex+1), - ]); - } - setOpen(false); + if (editorValue != null && editorValue != '' && editorValueIndex < 0) { + props.onChange([ + ...inputValue, + editorValue, + ]); + } else if (editorValue != null && editorValue != '') { + props.onChange([ + ...inputValue.slice(0, editorValueIndex), + editorValue, + ...inputValue.slice(editorValueIndex + 1), + ]); + } + setOpen(false); }; const onDelete = (index : number) => { const newValue : any[] = [ ...inputValue.slice(0, index), - ...inputValue.slice(index+1), + ...inputValue.slice(index + 1), ]; onChange(newValue); }; @@ -151,15 +155,15 @@ export const UiElementLeafList = (props: LeafListProps) => { { !props.readOnly ?
  • } - label={"Add"} + label={'Add'} className={classes.chip} size="small" color="secondary" onClick={ () => { setOpen(true); - setEditorValue(""); + setEditorValue(''); setEditorValueIndex(-1); - } + } } />
  • : null } @@ -172,24 +176,24 @@ export const UiElementLeafList = (props: LeafListProps) => { label={String(val)} onDelete={ !props.readOnly ? () => { onDelete(ind); } : undefined } onClick={ !props.readOnly ? () => { - setOpen(true); - setEditorValue(val); - setEditorValueIndex(ind); - } : undefined + setOpen(true); + setEditorValue(val); + setEditorValueIndex(ind); + } : undefined } /> - )) + )) } {/* { "Value is mandetory"} */}
    - {editorValueIndex < 0 ? "Add new value" : "Edit value" } + {editorValueIndex < 0 ? 'Add new value' : 'Edit value' } { ValueEditor && { - + diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementNumber.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementNumber.tsx index 76c11f6e5..b0342788f 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementNumber.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementNumber.tsx @@ -16,14 +16,14 @@ * ============LICENSE_END========================================================================== */ -import { ViewElementNumber } from "models/uiModels"; +import React from 'react'; +import { ViewElementNumber } from "../models/uiModels"; import { Tooltip, InputAdornment } from "@mui/material"; -import * as React from 'react'; import { BaseProps } from "./baseProps"; import { IfWhenTextInput } from "./ifWhenTextInput"; -import { checkRange } from "./verifyer"; +import { checkRange } from "../utilities/verifyer"; -type numberInputProps = BaseProps; +type numberInputProps = BaseProps; export const UiElementNumber = (props: numberInputProps) => { @@ -49,12 +49,12 @@ export const UiElementNumber = (props: numberInputProps) => { setError(true); setHelperText("Input is not a number."); } - props.onChange(data); + props.onChange(num); } return ( - createStyles({ +const useStyles = makeStyles(() => createStyles({ button: { - "justifyContent": "left" + 'justifyContent': 'left', }, })); @@ -37,16 +37,31 @@ type UIElementReferenceProps = { }; export const UIElementReference: React.FC = (props) => { - const classes = useStyles(); - const [disabled, setDisabled] = useState(true); const { element } = props; + const [disabled, setDisabled] = useState(true); + const classes = useStyles(); return ( - { ev.preventDefault(); ev.stopPropagation(); ev.button === 1 && setDisabled(!disabled) }}> + { + ev.preventDefault(); + ev.stopPropagation(); + if (ev.button === 1) { + setDisabled(!disabled); + } + }}> - + ); -} \ No newline at end of file +}; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementSelection.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementSelection.tsx index fdf803419..ebd04dab4 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementSelection.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementSelection.tsx @@ -16,45 +16,54 @@ * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; import { BaseProps } from './baseProps'; -import { ViewElementSelection } from '../models/uiModels' +import { ViewElementSelection } from '../models/uiModels'; import { FormControl, InputLabel, Select, FormHelperText, MenuItem, Tooltip } from '@mui/material'; type selectionProps = BaseProps; export const UiElementSelection = (props: selectionProps) => { - const element = props.value as ViewElementSelection; + const element = props.value as ViewElementSelection; - let error = ""; - const value = String(props.inputValue); - if (element.mandatory && Boolean(!value)) { - error = "Error"; - } + let error = ''; + const value = String(props.inputValue); + if (element.mandatory && Boolean(!value)) { + error = 'Error'; + } - return (props.readOnly || props.inputValue != null - ? ( - {element.label} + return (props.readOnly || props.inputValue != null + ? ( + {element.label} {error} ) - : null - ); -} \ No newline at end of file + : null + ); +}; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementString.tsx b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementString.tsx index 4908c41aa..8381d99a4 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementString.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/components/uiElementString.tsx @@ -21,7 +21,7 @@ import { Tooltip, TextField } from "@mui/material"; import { ViewElementString } from "../models/uiModels"; import { BaseProps } from "./baseProps"; import { IfWhenTextInput } from "./ifWhenTextInput"; -import { checkRange, checkPattern } from "./verifyer"; +import { checkRange, checkPattern } from "../utilities/verifyer"; type stringEntryProps = BaseProps ; @@ -69,7 +69,7 @@ export const UiElementString = (props: stringEntryProps) => { return ( - { }; return - { verifyValues(e.target.value) }} diff --git a/sdnr/wt/odlux/apps/configurationApp/src/components/verifyer.ts b/sdnr/wt/odlux/apps/configurationApp/src/components/verifyer.ts deleted file mode 100644 index 0a95cd8ca..000000000 --- a/sdnr/wt/odlux/apps/configurationApp/src/components/verifyer.ts +++ /dev/null @@ -1,280 +0,0 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 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 { Expression, YangRange, Operator, ViewElementNumber, ViewElementString, isViewElementNumber, isViewElementString } from '../models/uiModels'; - -export type validated = { isValid: boolean, error?: string } - -export type validatedRange = { isValid: boolean, error?: string }; - - -const rangeErrorStartNumber = "The entered number must be"; -const rangeErrorinnerMinTextNumber = "greater or equals than"; -const rangeErrorinnerMaxTextNumber = "less or equals than"; -const rangeErrorEndTextNumber = "."; - -const rangeErrorStartString = "The entered text must have"; -const rangeErrorinnerMinTextString = "no more than"; -const rangeErrorinnerMaxTextString = "less than"; -const rangeErrorEndTextString = " characters."; - -let errorMessageStart = ""; -let errorMessageMiddleMinPart = ""; -let errorMessageMiddleMaxPart = ""; -let errorMessageEnd = ""; - - -export function checkRange(element: ViewElementNumber | ViewElementString, data: number): string { - - //let test1: Operator = { operation: "AND", arguments: [{ operation: "OR", arguments: [{ operation: "AND", arguments: [new RegExp("^z", "g"), new RegExp("z$", "g")] }, new RegExp("^abc", "g"), new RegExp("^123", "g")] }, new RegExp("^def", "g"), new RegExp("^ppp", "g"), new RegExp("^aaa", "g")] }; - //let test1: Operator = { operation: "AND", arguments: [{ operation: "OR", arguments: [{ operation: "AND", arguments: [{ min: -5, max: 10 }, { min: -30, max: -20 }] }, { min: 8, max: 15 }] }] }; - //let test1: Operator = { operation: "OR", arguments: [{ operation: "OR", arguments: [{ min: -50, max: -40 }] }, { min: -30, max: -20 }, { min: 8, max: 15 }] }; - //let test1: Operator = { operation: "AND", arguments: [{ operation: "OR", arguments: [{ min: -5, max: 10 }, { min: 17, max: 23 }] }] }; - - const number = data; - - var expression = undefined; - - if (isViewElementString(element)) { - expression = element.length; - - errorMessageStart = rangeErrorStartString; - errorMessageMiddleMaxPart = rangeErrorinnerMaxTextString; - errorMessageMiddleMinPart = rangeErrorinnerMinTextString; - errorMessageEnd = rangeErrorEndTextString; - - } else if (isViewElementNumber(element)) { - expression = element.range; - - errorMessageStart = rangeErrorStartNumber; - errorMessageMiddleMaxPart = rangeErrorinnerMaxTextNumber; - errorMessageMiddleMinPart = rangeErrorinnerMinTextNumber; - errorMessageEnd = rangeErrorEndTextNumber; - } - - if (expression) { - if (isYangOperator(expression)) { - - const errorMessage = getRangeErrorMessages(expression, data); - return errorMessage; - - } else - if (isYangRange(expression)) { - - if (!isNaN(expression.min)) { - if (number < expression.min) { - return `${errorMessageStart} ${errorMessageMiddleMinPart} ${expression.min}${errorMessageEnd}`; - } - } - - if (!isNaN(expression.max)) { - if (number > expression.max) { - return `${errorMessageStart} ${errorMessageMiddleMaxPart} ${expression.max}${errorMessageEnd}`; - } - } - } - } - - - return ""; -} - -function isYangRange(val: YangRange | Operator): val is YangRange { - return (val as YangRange).min !== undefined; -} - -function isYangOperator(val: YangRange | Operator): val is Operator { - return (val as Operator).operation !== undefined; -} - -function getRangeErrorMessagesRecursively(value: Operator, data: number): string[] { - let currentItteration: string[] = []; - console.log(value); - - // itterate over all elements - for (let i = 0; i < value.arguments.length; i++) { - const element = value.arguments[i]; - - let min = undefined; - let max = undefined; - - let isNumberCorrect = false; - - if (isYangRange(element)) { - - //check found min values - if (!isNaN(element.min)) { - if (data < element.min) { - min = element.min; - } else { - isNumberCorrect = true; - } - } - - // check found max values - if (!isNaN(element.max)) { - if (data > element.max) { - max = element.max; - } else { - isNumberCorrect = true; - } - } - - // construct error messages - if (min != undefined) { - currentItteration.push(`${value.operation.toLocaleLowerCase()} ${errorMessageMiddleMinPart} ${min}`); - } else if (max != undefined) { - currentItteration.push(`${value.operation.toLocaleLowerCase()} ${errorMessageMiddleMaxPart} ${max}`); - - } - - } else if (isYangOperator(element)) { - - //get errormessages from expression - const result = getRangeErrorMessagesRecursively(element, data); - if (result.length === 0) { - isNumberCorrect = true; - } - currentItteration = currentItteration.concat(result); - } - - // if its an OR operation, the number has been checked and min/max are empty (thus not violated) - // delete everything found (because at least one found is correct, therefore all are correct) and break from loop - if (min === undefined && max === undefined && isNumberCorrect && value.operation === "OR") { - - currentItteration.splice(0, currentItteration.length); - break; - } - } - - return currentItteration; -} - -function getRangeErrorMessages(value: Operator, data: number): string { - - const currentItteration = getRangeErrorMessagesRecursively(value, data); - - // build complete error message from found parts - let errormessage = ""; - if (currentItteration.length > 1) { - - currentItteration.forEach((element, index) => { - if (index === 0) { - errormessage = createStartMessage(element); - } else if (index === currentItteration.length - 1) { - errormessage += ` ${element}${errorMessageEnd}`; - } else { - errormessage += `, ${element}` - } - }); - } else if (currentItteration.length == 1) { - errormessage = `${createStartMessage(currentItteration[0])}${errorMessageEnd}`; - } - - return errormessage; -} - -function createStartMessage(element: string) { - - //remove leading or or and from text - if (element.startsWith("and")) - element = element.replace("and", ""); - else if (element.startsWith("or")) - element = element.replace("or", ""); - - return `${errorMessageStart} ${element}`; -} - -export const checkPattern = (expression: RegExp | Operator | undefined, data: string): validated => { - - if (expression) { - if (isRegExp(expression)) { - const isValid = expression.test(data); - if (!isValid) - return { isValid: isValid, error: "The input is in a wrong format." }; - - } else if (isRegExpOperator(expression)) { - const result = isPatternValid(expression, data); - - if (!result) { - return { isValid: false, error: "The input is in a wrong format." }; - } - } - } - - return { isValid: true } -} - -function getRegexRecursively(value: Operator, data: string): boolean[] { - let currentItteration: boolean[] = []; - for (let i = 0; i < value.arguments.length; i++) { - const element = value.arguments[i]; - if (isRegExp(element)) { - // if regex is found, add it to list - currentItteration.push(element.test(data)) - } else if (isRegExpOperator(element)) { - //if RegexExpression is found, try to get regex from it - currentItteration = currentItteration.concat(getRegexRecursively(element, data)); - } - } - - if (value.operation === "OR") { - // if one is true, all are true, all found items can be discarded - let result = currentItteration.find(element => element); - if (result) { - return []; - } - } - return currentItteration; -} - -function isPatternValid(value: Operator, data: string): boolean { - - - // get all regex - const result = getRegexRecursively(value, data); - console.log(value); - - - if (value.operation === "AND") { - // if AND operation is executed... - // no element can be false - const check = result.find(element => element !== true); - if (check) - return false; - else - return true; - } else { - // if OR operation is executed... - // ... just one element must be true - const check = result.find(element => element === true); - if (check) - return true; - else - return false; - - } -} - -function isRegExp(val: RegExp | Operator): val is RegExp { - return (val as RegExp).source !== undefined; -} - -function isRegExpOperator(val: RegExp | Operator): val is Operator { - return (val as Operator).operation !== undefined; -} \ 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 index 1af699a6b..9cbd9163e 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/configurationAppRootHandler.ts @@ -19,9 +19,9 @@ 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"; +import { IDeviceDescriptionState, deviceDescriptionHandler } from './deviceDescriptionHandler'; +import { IViewDescriptionState, viewDescriptionHandler } from './viewDescriptionHandler'; +import { IValueSelectorState, valueSelectorHandler } from './valueSelectorHandler'; interface IConfigurationAppStoreState { connectedNetworkElements: IConnectedNetworkElementsState; // used for ne selection @@ -32,7 +32,7 @@ interface IConfigurationAppStoreState { declare module '../../../../framework/src/store/applicationStore' { interface IApplicationStoreState { - configuration: IConfigurationAppStoreState, + configuration: IConfigurationAppStoreState; } } diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts index 8ca8fdf27..d2863dd2e 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/connectedNetworkElementsHandler.ts @@ -25,8 +25,8 @@ import { restService } from '../services/restServices'; export interface IConnectedNetworkElementsState extends IExternalTableState { } -// create eleactic search material data fetch handler -const connectedNetworkElementsSearchHandler = createSearchDataHandler('network-element-connection', false, { status: "Connected" }); +// create elastic search material data fetch handler +const connectedNetworkElementsSearchHandler = createSearchDataHandler('network-element-connection', false, { status: 'Connected' }); export const { actionHandler: connectedNetworkElementsActionHandler, @@ -41,5 +41,5 @@ export const { const neUrl = restService.getNetworkElementUri(ne.id); const policy = getAccessPolicyByUrl(neUrl); return !(policy.GET && policy.POST); - } + }, ); diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts index 408399da4..cd01b0988 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/deviceDescriptionHandler.ts @@ -16,23 +16,23 @@ * ============LICENSE_END========================================================================== */ -import { Module } from "../models/yang"; -import { ViewSpecification } from "../models/uiModels"; -import { IActionHandler } from "../../../../framework/src/flux/action"; -import { UpdateDeviceDescription } from "../actions/deviceActions"; +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, + nodeId: string; modules: { - [name: string]: Module - }, - views: ViewSpecification[], + [name: string]: Module; + }; + views: ViewSpecification[]; } const deviceDescriptionStateInit: IDeviceDescriptionState = { - nodeId: "", + nodeId: '', modules: {}, - views: [] + views: [], }; export const deviceDescriptionHandler: IActionHandler = (state = deviceDescriptionStateInit, action) => { @@ -41,7 +41,7 @@ export const deviceDescriptionHandler: IActionHandler = ...state, nodeId: action.nodeId, modules: action.modules, - views: action.views + 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 index 5b2d55ee2..70d5eb253 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/valueSelectorHandler.ts @@ -16,9 +16,9 @@ * ============LICENSE_END========================================================================== */ -import { IActionHandler } from "../../../../framework/src/flux/action"; -import { ViewSpecification } from "../models/uiModels"; -import { EnableValueSelector, SetSelectedValue, UpdateDeviceDescription, SetCollectingSelectionData, UpdatViewDescription, UpdatOutputData } from "../actions/deviceActions"; +import { IActionHandler } from '../../../../framework/src/flux/action'; +import { ViewSpecification } from '../models/uiModels'; +import { EnableValueSelector, SetSelectedValue, UpdateDeviceDescription, SetCollectingSelectionData, UpdateViewDescription, UpdateOutputData } from '../actions/deviceActions'; export interface IValueSelectorState { collectingData: boolean; @@ -28,13 +28,13 @@ export interface IValueSelectorState { onValueSelected: (value: any) => void; } -const nc = (val: React.SyntheticEvent) => { }; +const dummyFunc = () => { }; const valueSelectorStateInit: IValueSelectorState = { collectingData: false, keyProperty: undefined, listSpecification: null, listData: [], - onValueSelected: nc, + onValueSelected: dummyFunc, }; export const valueSelectorHandler: IActionHandler = (state = valueSelectorStateInit, action) => { @@ -53,22 +53,24 @@ export const valueSelectorHandler: IActionHandler = (state listData: action.listData, }; } else if (action instanceof SetSelectedValue) { - state.keyProperty && state.onValueSelected(action.value[state.keyProperty]); + if (state.keyProperty) { + state.onValueSelected(action.value[state.keyProperty]); + } state = { ...state, collectingData: false, keyProperty: undefined, listSpecification: null, - onValueSelected: nc, + onValueSelected: dummyFunc, listData: [], }; - } else if (action instanceof UpdateDeviceDescription || action instanceof UpdatViewDescription || action instanceof UpdatOutputData) { + } else if (action instanceof UpdateDeviceDescription || action instanceof UpdateViewDescription || action instanceof UpdateOutputData) { state = { ...state, collectingData: false, keyProperty: undefined, listSpecification: null, - onValueSelected: nc, + onValueSelected: dummyFunc, listData: [], }; } diff --git a/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts b/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts index ff85a97ea..39b47be84 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/handlers/viewDescriptionHandler.ts @@ -16,18 +16,18 @@ * ============LICENSE_END========================================================================== */ -import { IActionHandler } from "../../../../framework/src/flux/action"; +import { IActionHandler } from '../../../../framework/src/flux/action'; -import { UpdatViewDescription, UpdatOutputData } from "../actions/deviceActions"; -import { ViewSpecification } from "../models/uiModels"; +import { UpdateViewDescription, UpdateOutputData } from '../actions/deviceActions'; +import { ViewSpecification } from '../models/uiModels'; export enum DisplayModeType { doNotDisplay = 0, displayAsObject = 1, displayAsList = 2, displayAsRPC = 3, - displayAsMessage = 4 -}; + displayAsMessage = 4, +} export type DisplaySpecification = { displayMode: DisplayModeType.doNotDisplay; @@ -45,13 +45,13 @@ export type DisplaySpecification = { } | { displayMode: DisplayModeType.displayAsMessage; renderMessage: string; -} +}; export interface IViewDescriptionState { vPath: string | null; displaySpecification: DisplaySpecification; - viewData: any, - outputData?: any, + viewData: any; + outputData?: any; } const viewDescriptionStateInit: IViewDescriptionState = { @@ -64,7 +64,7 @@ const viewDescriptionStateInit: IViewDescriptionState = { }; export const viewDescriptionHandler: IActionHandler = (state = viewDescriptionStateInit, action) => { - if (action instanceof UpdatViewDescription) { + if (action instanceof UpdateViewDescription) { state = { ...state, vPath: action.vPath, @@ -72,7 +72,7 @@ export const viewDescriptionHandler: IActionHandler = (st outputData: undefined, displaySpecification: action.displaySpecification, }; - } else if (action instanceof UpdatOutputData) { + } else if (action instanceof UpdateOutputData) { state = { ...state, outputData: action.outputData, diff --git a/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts b/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts index 88f70181c..e1ef1ea2d 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/models/networkElementConnection.ts @@ -24,7 +24,7 @@ export type NetworkElementConnection = { username?: string; password?: string; isRequired?: boolean; - status?: "connected" | "mounted" | "unmounted" | "connecting" | "disconnected" | "idle"; + status?: 'connected' | 'mounted' | 'unmounted' | 'connecting' | 'disconnected' | 'idle'; coreModelCapability?: string; deviceType?: string; nodeDetails?: { @@ -33,5 +33,5 @@ export type NetworkElementConnection = { 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 index 29484d812..7d9e63caf 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/models/uiModels.ts @@ -16,128 +16,130 @@ * ============LICENSE_END========================================================================== */ +import type { WhenAST } from '../yang/whenParser'; + export type ViewElementBase = { - "id": string; - "label": string; - "module": string; - "path": 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 -} + 'id': string; + 'label': string; + 'module': string; + 'path': string; + 'config': boolean; + 'ifFeature'?: string; + 'when'?: WhenAST; + '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"?: Expression; // number of octets -} + 'uiType': 'binary'; + 'length'?: Expression; // number of octets +}; // https://tools.ietf.org/html/rfc7950#section-9.7.4 export type ViewElementBits = ViewElementBase & { - "uiType": "bits"; - "flags": { + 'uiType': 'bits'; + 'flags': { [name: string]: number | undefined; // 0 - 4294967295 - } -} + }; +}; // https://tools.ietf.org/html/rfc7950#section-9 export type ViewElementString = ViewElementBase & { - "uiType": "string"; - "pattern"?: Expression; - "length"?: Expression; - "invertMatch"?: true; -} + 'uiType': 'string'; + 'pattern'?: Expression; + 'length'?: Expression; + 'invertMatch'?: true; +}; // special case derived from export type ViewElementDate = ViewElementBase & { - "uiType": "date"; - "pattern"?: Expression; - "length"?: Expression; - "invertMatch"?: true; -} + 'uiType': 'date'; + 'pattern'?: Expression; + 'length'?: Expression; + 'invertMatch'?: true; +}; // https://tools.ietf.org/html/rfc7950#section-9.3 export type ViewElementNumber = ViewElementBase & { - "uiType": "number"; - "min": number; - "max": number; - "range"?: Expression; - "units"?: string; - "format"?: string; - "fDigits"?: number; -} + 'uiType': 'number'; + 'min': number; + 'max': number; + 'range'?: Expression; + '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; -} + '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, + '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; -} + '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; + 'uiType': 'object'; + 'isList': true; + 'viewId': string; + 'key'?: string; }); export type ViewElementReference = ViewElementBase & { - "uiType": "reference"; - "referencePath": string; - "ref": (currentPath: string) => [ViewElement , string] | undefined; -} + 'uiType': 'reference'; + 'referencePath': string; + 'ref': (currentPath: string) => [ViewElement, string] | undefined; +}; export type ViewElementUnion = ViewElementBase & { - "uiType": "union"; - "elements": ViewElement[]; -} + 'uiType': 'union'; + 'elements': ViewElement[]; +}; -export type ViewElementChoiseCase = { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } }; +export type ViewElementChoiceCase = { id: string; label: string; description?: string; elements: { [name: string]: ViewElement } }; -export type ViewElementChoise = ViewElementBase & { - "uiType": "choise"; - "cases": { - [name: string]: ViewElementChoiseCase; - } -} +export type ViewElementChoice = ViewElementBase & { + 'uiType': 'choice'; + 'cases': { + [name: string]: ViewElementChoiceCase; + }; +}; // https://tools.ietf.org/html/rfc7950#section-7.14.1 export type ViewElementRpc = ViewElementBase & { - "uiType": "rpc"; - "inputViewId"?: string; - "outputViewId"?: string; -} + 'uiType': 'rpc'; + 'inputViewId'?: string; + 'outputViewId'?: string; +}; export type ViewElementEmpty = ViewElementBase & { - "uiType": "empty"; -} + 'uiType': 'empty'; +}; export type ViewElement = | ViewElementEmpty @@ -152,88 +154,88 @@ export type ViewElement = | ViewElementSelection | ViewElementReference | ViewElementUnion - | ViewElementChoise + | ViewElementChoice | ViewElementRpc; export const isViewElementString = (viewElement: ViewElement): viewElement is ViewElementString => { - return viewElement && (viewElement.uiType === "string" || viewElement.uiType === "date"); -} + return viewElement && (viewElement.uiType === 'string' || viewElement.uiType === 'date'); +}; export const isViewElementDate = (viewElement: ViewElement): viewElement is ViewElementDate => { - return viewElement && (viewElement.uiType === "date"); -} + return viewElement && (viewElement.uiType === 'date'); +}; export const isViewElementNumber = (viewElement: ViewElement): viewElement is ViewElementNumber => { - return viewElement && viewElement.uiType === "number"; -} + return viewElement && viewElement.uiType === 'number'; +}; export const isViewElementBoolean = (viewElement: ViewElement): viewElement is ViewElementBoolean => { - return viewElement && viewElement.uiType === "boolean"; -} + return viewElement && viewElement.uiType === 'boolean'; +}; export const isViewElementObject = (viewElement: ViewElement): viewElement is ViewElementObject => { - return viewElement && viewElement.uiType === "object" && viewElement.isList === false; -} + return viewElement && viewElement.uiType === 'object' && viewElement.isList === false; +}; export const isViewElementList = (viewElement: ViewElement): viewElement is ViewElementList => { - return viewElement && viewElement.uiType === "object" && viewElement.isList === true; -} + return viewElement && viewElement.uiType === 'object' && viewElement.isList === true; +}; export const isViewElementObjectOrList = (viewElement: ViewElement): viewElement is ViewElementObject | ViewElementList => { - return viewElement && viewElement.uiType === "object"; -} + return viewElement && viewElement.uiType === 'object'; +}; export const isViewElementSelection = (viewElement: ViewElement): viewElement is ViewElementSelection => { - return viewElement && viewElement.uiType === "selection"; -} + return viewElement && viewElement.uiType === 'selection'; +}; export const isViewElementReference = (viewElement: ViewElement): viewElement is ViewElementReference => { - return viewElement && viewElement.uiType === "reference"; -} + return viewElement && viewElement.uiType === 'reference'; +}; export const isViewElementUnion = (viewElement: ViewElement): viewElement is ViewElementUnion => { - return viewElement && viewElement.uiType === "union"; -} + return viewElement && viewElement.uiType === 'union'; +}; -export const isViewElementChoise = (viewElement: ViewElement): viewElement is ViewElementChoise => { - return viewElement && viewElement.uiType === "choise"; -} +export const isViewElementChoice = (viewElement: ViewElement): viewElement is ViewElementChoice => { + return viewElement && viewElement.uiType === 'choice'; +}; export const isViewElementRpc = (viewElement: ViewElement): viewElement is ViewElementRpc => { - return viewElement && viewElement.uiType === "rpc"; -} + return viewElement && viewElement.uiType === 'rpc'; +}; export const isViewElementEmpty = (viewElement: ViewElement): viewElement is ViewElementRpc => { - return viewElement && viewElement.uiType === "empty"; -} + return viewElement && viewElement.uiType === 'empty'; +}; -export const ResolveFunction = Symbol("IsResolved"); +export const ResolveFunction = Symbol('IsResolved'); export type ViewSpecification = { - "id": string; - "ns"?: string; - "name"?: string; - "title"?: string; - "parentView"?: string; - "language": string; - "ifFeature"?: string; - "when"?: string; - "uses"?: (string[]) & { [ResolveFunction]?: (parent: string) => void }; - "elements": { [name: string]: ViewElement }; - "config": boolean; - readonly "canEdit": boolean; -} + id: string; + ns?: string; + name?: string; + title?: string; + parentView?: string; + language: string; + ifFeature?: string; + when?: WhenAST; + uses?: (string[]) & { [ResolveFunction]?: (parent: string) => void }; + elements: { [name: string]: ViewElement }; + config: boolean; + readonly canEdit: boolean; +}; export type YangRange = { - min: number, - max: number, -} + min: number; + max: number; +}; export type Expression = | T | Operator; export type Operator = { - operation: "AND" | "OR"; + operation: 'AND' | 'OR'; arguments: Expression[]; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts b/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts index 79704ae34..e4e59fb96 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/models/yang.ts @@ -16,7 +16,7 @@ * ============LICENSE_END========================================================================== */ -import { ViewElement, ViewSpecification } from "./uiModels"; +import { ViewElement, ViewSpecification } from './uiModels'; export enum ModuleState { stable, @@ -30,27 +30,27 @@ export type Token = { 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[], -} + id: string; + label: string; + base?: string; + description?: string; + reference?: string; + children?: Identity[]; + values?: Identity[]; +}; export type Revision = { - description?: string, - reference?: string + description?: string; + reference?: string; }; export type Module = { @@ -68,4 +68,4 @@ export type Module = { views: { [view: string]: ViewSpecification }; elements: { [view: string]: ViewElement }; executionOrder?: number; -} \ No newline at end of file +}; \ 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 index e37879102..7dd2d6ae4 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/pluginConfiguration.tsx @@ -16,71 +16,74 @@ * ============LICENSE_END========================================================================== */ -import * as React from "react"; +import 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 { 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"; -import { DisplayModeType } from "./handlers/viewDescriptionHandler"; -import { ViewSpecification } from "./models/uiModels"; +import { configurationAppRootHandler } from './handlers/configurationAppRootHandler'; +import { NetworkElementSelector } from './views/networkElementSelector'; + +import ConfigurationApplication from './views/configurationApplication'; +import { updateNodeIdAsyncActionCreator, updateViewActionAsyncCreator } from './actions/deviceActions'; +import { DisplayModeType } from './handlers/viewDescriptionHandler'; +import { ViewSpecification } from './models/uiModels'; + +const appIcon = require('./assets/icons/configurationAppIcon.svg'); // select app icon let currentNodeId: string | null | undefined = undefined; let currentVirtualPath: string | null | undefined = undefined; let lastUrl: string | undefined = undefined; -const mapDisp = (dispatcher: IDispatcher) => ({ +const mapDispatch = (dispatcher: IDispatcher) => ({ updateNodeId: (nodeId: string) => dispatcher.dispatch(updateNodeIdAsyncActionCreator(nodeId)), updateView: (vPath: string) => dispatcher.dispatch(updateViewActionAsyncCreator(vPath)), }); -const ConfigurationApplicationRouteAdapter = connect(undefined, mapDisp)((props: RouteComponentProps<{ nodeId?: string, 0: string }> & Connect) => { +// eslint-disable-next-line @typescript-eslint/naming-convention +const ConfigurationApplicationRouteAdapter = connect(undefined, mapDispatch)((props: RouteComponentProps<{ nodeId?: string; 0: string }> & Connect) => { 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 + // ensure the asynchronous update will only be called once per path lastUrl = props.location.pathname; window.setTimeout(async () => { // check if the nodeId has changed - let dump = false; + let enableDump = false; if (currentNodeId !== props.match.params.nodeId) { currentNodeId = props.match.params.nodeId || undefined; - if (currentNodeId && currentNodeId.endsWith("|dump")) { - dump = true; + if (currentNodeId && currentNodeId.endsWith('|dump')) { + enableDump = true; currentNodeId = currentNodeId.replace(/\|dump$/i, ''); } currentVirtualPath = null; - currentNodeId && (await props.updateNodeId(currentNodeId)); + if (currentNodeId) { + await props.updateNodeId(currentNodeId); + } } if (currentVirtualPath !== props.match.params[0]) { currentVirtualPath = props.match.params[0]; - if (currentVirtualPath && currentVirtualPath.endsWith("|dump")) { - dump = true; + if (currentVirtualPath && currentVirtualPath.endsWith('|dump')) { + enableDump = true; currentVirtualPath = currentVirtualPath.replace(/\|dump$/i, ''); } await props.updateView(currentVirtualPath); } - if (dump) { + if (enableDump) { const device = props.state.configuration.deviceDescription; const ds = props.state.configuration.viewDescription.displaySpecification; const createDump = (view: ViewSpecification | null, level: number = 0) => { - if (view === null) return "Empty"; + if (view === null) return 'Empty'; const indention = Array(level * 4).fill(' ').join(''); let result = ''; @@ -88,24 +91,24 @@ const ConfigurationApplicationRouteAdapter = connect(undefined, mapDisp)((props: // result += `${indention} [${view.canEdit ? 'rw' : 'ro'}] ${view.ns}:${view.name} ${ds.displayMode === DisplayModeType.displayAsList ? '[LIST]' : ''}\r\n`; result += Object.keys(view.elements).reduce((acc, cur) => { const elm = view.elements[cur]; - acc += `${indention} [${elm.uiType === "rpc" ? "x" : elm.config ? 'rw' : 'ro'}:${elm.id}] (${elm.module}:${elm.label}) {${elm.uiType}} ${elm.uiType === "object" && elm.isList ? `as LIST with KEY [${elm.key}]` : ""}\r\n`; - // acc += `${indention} +${elm.mandatory ? "mandetory" : "none"} - ${elm.path} \r\n`; + acc += `${indention} [${elm.uiType === 'rpc' ? 'x' : elm.config ? 'rw' : 'ro'}:${elm.id}] (${elm.module}:${elm.label}) {${elm.uiType}} ${elm.uiType === 'object' && elm.isList ? `as LIST with KEY [${elm.key}]` : ''}\r\n`; + // acc += `${indention} +${elm.mandatory ? "mandatory" : "none"} - ${elm.path} \r\n`; switch (elm.uiType) { - case "object": + case 'object': acc += createDump(device.views[(elm as any).viewId], level + 1); break; default: } return acc; - }, ""); + }, ''); return `${result}`; - } + }; const dump = createDump(ds.displayMode === DisplayModeType.displayAsObject || ds.displayMode === DisplayModeType.displayAsList ? ds.viewSpecification : null, 0); - var element = document.createElement('a'); + const element = document.createElement('a'); element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(dump)); - element.setAttribute('download', currentNodeId + ".txt"); + element.setAttribute('download', currentNodeId + '.txt'); element.style.display = 'none'; document.body.appendChild(element); @@ -133,10 +136,10 @@ const App = withRouter((props: RouteComponentProps) => ( export function register() { applicationManager.registerApplication({ - name: "configuration", - icon: faAdjust, + name: 'configuration', + icon: appIcon, rootComponent: App, rootActionHandler: configurationAppRootHandler, - menuEntry: "Configuration" + menuEntry: 'Configuration', }); } diff --git a/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts b/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts index 02060ef12..07e263559 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/services/restServices.ts @@ -16,79 +16,79 @@ * ============LICENSE_END========================================================================== */ -import { requestRest, requestRestExt } from "../../../../framework/src/services/restService"; -import { convertPropertyNames, replaceHyphen } from "../../../../framework/src/utilities/yangHelper"; +import { requestRest, requestRestExt } from '../../../../framework/src/services/restService'; +import { convertPropertyNames, replaceHyphen } from '../../../../framework/src/utilities/yangHelper'; -import { NetworkElementConnection } from "../models/networkElementConnection"; +import { NetworkElementConnection } from '../models/networkElementConnection'; type ImportOnlyResponse = { - "ietf-yang-library:yang-library": { - "module-set": { - "import-only-module": { - "name": string, - "revision": string, - }[], - }[], - }, -} + 'ietf-yang-library:yang-library': { + 'module-set': { + 'import-only-module': { + 'name': string; + 'revision': string; + }[]; + }[]; + }; +}; type CapabilityResponse = { - "network-topology:node": { - "node-id": string, - "netconf-node-topology:available-capabilities": { - "available-capability": { - "capability-origin": string, - "capability": string, - }[] - }, - "netconf-node-topology:unavailable-capabilities": { - "unavailable-capability": { - "capability": string, - "failure-reason": string, - }[] - } - }[] -} + 'network-topology:node': { + 'node-id': string; + 'netconf-node-topology:available-capabilities': { + 'available-capability': { + 'capability-origin': string; + 'capability': string; + }[]; + }; + 'netconf-node-topology:unavailable-capabilities': { + 'unavailable-capability': { + 'capability': string; + 'failure-reason': string; + }[]; + }; + }[]; +}; type CapabilityAnswer = { availableCapabilities: { - capabilityOrigin: string, - capability: string, - version: string, - }[] | null, + capabilityOrigin: string; + capability: string; + version: string; + }[] | null; unavailableCapabilities: { - failureReason: string, - capability: string, - version: string, - }[] | null, + failureReason: string; + capability: string; + version: string; + }[] | null; importOnlyModules: { - name: string, - revision: string, - }[] | null -} + name: string; + revision: string; + }[] | null; +}; const capParser = /^\(.*\?revision=(\d{4}-\d{2}-\d{2})\)(\S+)$/i; class RestService { public getNetworkElementUri = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId; - public async getImportOnlyModules(nodeId: string): Promise<{ name: string, revision: string }[]> { + public async getImportOnlyModules(nodeId: string): Promise<{ name: string; revision: string }[]> { const path = `${this.getNetworkElementUri(nodeId)}/yang-ext:mount/ietf-yang-library:yang-library?content=nonconfig&fields=module-set(import-only-module(name;revision))`; - const importOnlyResult = await requestRest(path, { method: "GET" }); + const importOnlyResult = await requestRest(path, { method: 'GET' }); const importOnlyModules = importOnlyResult - ? importOnlyResult["ietf-yang-library:yang-library"]["module-set"][0]["import-only-module"] + ? importOnlyResult['ietf-yang-library:yang-library']['module-set'][0]['import-only-module'] : []; return importOnlyModules; } public async getCapabilitiesByMountId(nodeId: string): Promise { const path = this.getNetworkElementUri(nodeId); - const capabilitiesResult = await requestRest(path, { method: "GET" }); - const availableCapabilities = capabilitiesResult && capabilitiesResult["network-topology:node"] && capabilitiesResult["network-topology:node"].length > 0 && - (capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"] && - capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"]["available-capability"] && - capabilitiesResult["network-topology:node"][0]["netconf-node-topology:available-capabilities"]["available-capability"].map(obj => convertPropertyNames(obj, replaceHyphen)) || []) + const capabilitiesResult = await requestRest(path, { method: 'GET' }); + const availableCapabilities = capabilitiesResult && capabilitiesResult['network-topology:node'] && capabilitiesResult['network-topology:node'].length > 0 && + (capabilitiesResult['network-topology:node'][0]['netconf-node-topology:available-capabilities'] && + capabilitiesResult['network-topology:node'][0]['netconf-node-topology:available-capabilities']['available-capability'] && + capabilitiesResult['network-topology:node'][0]['netconf-node-topology:available-capabilities']['available-capability'].map(obj => convertPropertyNames(obj, replaceHyphen)) || []) .map(cap => { const capMatch = cap && capParser.exec(cap.capability); return capMatch ? { @@ -98,20 +98,20 @@ class RestService { } : null ; }).filter((cap) => cap != null) || [] as any; - const unavailableCapabilities = capabilitiesResult && capabilitiesResult["network-topology:node"] && capabilitiesResult["network-topology:node"].length > 0 && - (capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"] && - capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"]["unavailable-capability"] && - capabilitiesResult["network-topology:node"][0]["netconf-node-topology:unavailable-capabilities"]["unavailable-capability"].map(obj => convertPropertyNames(obj, replaceHyphen)) || []) - .map(cap => { - const capMatch = cap && capParser.exec(cap.capability); - return capMatch ? { - failureReason: cap.failureReason, - capability: capMatch && capMatch[2] || '', - version: capMatch && capMatch[1] || '', - } : null ; - }).filter((cap) => cap != null) || [] as any; - - const importOnlyModules = availableCapabilities && availableCapabilities.findIndex((ac: {capability: string }) => ac.capability && ac.capability.toLowerCase() === "ietf-yang-library") > -1 + const unavailableCapabilities = capabilitiesResult && capabilitiesResult['network-topology:node'] && capabilitiesResult['network-topology:node'].length > 0 && + (capabilitiesResult['network-topology:node'][0]['netconf-node-topology:unavailable-capabilities'] && + capabilitiesResult['network-topology:node'][0]['netconf-node-topology:unavailable-capabilities']['unavailable-capability'] && + capabilitiesResult['network-topology:node'][0]['netconf-node-topology:unavailable-capabilities']['unavailable-capability'].map(obj => convertPropertyNames(obj, replaceHyphen)) || []) + .map(cap => { + const capMatch = cap && capParser.exec(cap.capability); + return capMatch ? { + failureReason: cap.failureReason, + capability: capMatch && capMatch[2] || '', + version: capMatch && capMatch[1] || '', + } : null ; + }).filter((cap) => cap != null) || [] as any; + + const importOnlyModules = availableCapabilities && availableCapabilities.findIndex((ac: { capability: string }) => ac.capability && ac.capability.toLowerCase() === 'ietf-yang-library') > -1 ? await this.getImportOnlyModules(nodeId) : null; @@ -123,11 +123,11 @@ class RestService { // const connectedNetworkElement = await requestRest(path, { method: "GET" }); // return connectedNetworkElement || null; - const path = "/rests/operations/data-provider:read-network-element-connection-list"; - const body = { "data-provider:input": { "filter": [{ "property": "node-id", "filtervalue": nodeId }], "sortorder": [], "pagination": { "size": 1, "page": 1 } } }; - const networkElementResult = await requestRest<{ "data-provider:output": { data: NetworkElementConnection[] } }>(path, { method: "POST", body: JSON.stringify(body) }); - return networkElementResult && networkElementResult["data-provider:output"] && networkElementResult["data-provider:output"].data && - networkElementResult["data-provider:output"].data.map(obj => convertPropertyNames(obj, replaceHyphen))[0] || null; + const path = '/rests/operations/data-provider:read-network-element-connection-list'; + const body = { 'data-provider:input': { 'filter': [{ 'property': 'node-id', 'filtervalue': nodeId }], 'sortorder': [], 'pagination': { 'size': 1, 'page': 1 } } }; + const networkElementResult = await requestRest<{ 'data-provider:output': { data: NetworkElementConnection[] } }>(path, { method: 'POST', body: JSON.stringify(body) }); + return networkElementResult && networkElementResult['data-provider:output'] && networkElementResult['data-provider:output'].data && + networkElementResult['data-provider:output'].data.map(obj => convertPropertyNames(obj, replaceHyphen))[0] || null; } /** Reads the config data by restconf path. @@ -135,7 +135,7 @@ class RestService { * @returns The data. */ public getConfigData(path: string) { - return requestRestExt<{ [key: string]: any }>(path, { method: "GET" }); + return requestRestExt<{ [key: string]: any }>(path, { method: 'GET' }); } /** Updates or creates the config data by restconf path using data. @@ -144,11 +144,11 @@ class RestService { * @returns The written data. */ public setConfigData(path: string, data: any) { - return requestRestExt<{ [key: string]: any }>(path, { method: "PUT", body: JSON.stringify(data) }); + return requestRestExt<{ [key: string]: any }>(path, { method: 'PUT', body: JSON.stringify(data) }); } public executeRpc(path: string, data: any) { - return requestRestExt<{ [key: string]: any }>(path, { method: "POST", body: JSON.stringify(data) }); + return requestRestExt<{ [key: string]: any }>(path, { method: 'POST', body: JSON.stringify(data) }); } /** Removes the element by restconf path. @@ -156,7 +156,7 @@ class RestService { * @returns The restconf result. */ public removeConfigElement(path: string) { - return requestRestExt<{ [key: string]: any }>(path, { method: "DELETE" }); + return requestRestExt<{ [key: string]: any }>(path, { method: 'DELETE' }); } } diff --git a/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts b/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts index b81a92c14..bbd051aeb 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/services/yangService.ts @@ -16,28 +16,22 @@ * ============LICENSE_END========================================================================== */ -type YangInfo = [string, (string | null | undefined)]; +const cache: { [path: string]: string } = { }; +const getCapability = async (capability: string, nodeId: string, version?: string) => { + const url = `/yang-schema/${capability}${version ? `/${version}` : ''}?node=${nodeId}`; -const cache: { [path: string]: string } = { + const cacheHit = cache[url]; + if (cacheHit) return cacheHit; -}; - -class YangService { - - public async getCapability(capability: string, nodeId: string, version?: string) { - const url = `/yang-schema/${capability}${version ? `/${version}` : ""}?node=${nodeId}`; - - 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; + 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 const yangService = { + getCapability, +}; export default yangService; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/utilities/verifyer.ts b/sdnr/wt/odlux/apps/configurationApp/src/utilities/verifyer.ts new file mode 100644 index 000000000..9dd12031f --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/utilities/verifyer.ts @@ -0,0 +1,261 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 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 { YangRange, Operator, ViewElementNumber, ViewElementString, isViewElementNumber, isViewElementString } from '../models/uiModels'; + +export type validated = { isValid: boolean; error?: string }; + +export type validatedRange = { isValid: boolean; error?: string }; + +const rangeErrorStartNumber = 'The entered number must be'; +const rangeErrorInnerMinTextNumber = 'greater or equals than'; +const rangeErrorInnerMaxTextNumber = 'less or equals than'; +const rangeErrorEndTextNumber = '.'; + +const rangeErrorStartString = 'The entered text must have'; +const rangeErrorInnerMinTextString = 'no more than'; +const rangeErrorInnerMaxTextString = 'less than'; +const rangeErrorEndTextString = ' characters.'; + +let errorMessageStart = ''; +let errorMessageMiddleMinPart = ''; +let errorMessageMiddleMaxPart = ''; +let errorMessageEnd = ''; + +const isYangRange = (val: YangRange | Operator): val is YangRange => (val as YangRange).min !== undefined; + +const isYangOperator = (val: YangRange | Operator): val is Operator => (val as Operator).operation !== undefined; + +const isRegExp = (val: RegExp | Operator): val is RegExp => (val as RegExp).source !== undefined; + +const isRegExpOperator = (val: RegExp | Operator): val is Operator => (val as Operator).operation !== undefined; + +const getRangeErrorMessagesRecursively = (value: Operator, data: number): string[] => { + let currentIteration: string[] = []; + + // iterate over all elements + for (let i = 0; i < value.arguments.length; i++) { + const element = value.arguments[i]; + + let min = undefined; + let max = undefined; + + let isNumberCorrect = false; + + if (isYangRange(element)) { + + //check found min values + if (!isNaN(element.min)) { + if (data < element.min) { + min = element.min; + } else { + isNumberCorrect = true; + } + } + + // check found max values + if (!isNaN(element.max)) { + if (data > element.max) { + max = element.max; + } else { + isNumberCorrect = true; + } + } + + // construct error messages + if (min != undefined) { + currentIteration.push(`${value.operation.toLocaleLowerCase()} ${errorMessageMiddleMinPart} ${min}`); + } else if (max != undefined) { + currentIteration.push(`${value.operation.toLocaleLowerCase()} ${errorMessageMiddleMaxPart} ${max}`); + + } + + } else if (isYangOperator(element)) { + + //get error_message from expression + const result = getRangeErrorMessagesRecursively(element, data); + if (result.length === 0) { + isNumberCorrect = true; + } + currentIteration = currentIteration.concat(result); + } + + // if its an OR operation, the number has been checked and min/max are empty (thus not violated) + // delete everything found (because at least one found is correct, therefore all are correct) and break from loop + if (min === undefined && max === undefined && isNumberCorrect && value.operation === 'OR') { + + currentIteration.splice(0, currentIteration.length); + break; + } + } + + return currentIteration; +}; + +const createStartMessage = (element: string) => { + //remove leading or or and from text + if (element.startsWith('and')) { + element = element.replace('and', ''); + } else if (element.startsWith('or')) { + element = element.replace('or', ''); + } + return `${errorMessageStart} ${element}`; +}; + +const getRangeErrorMessages = (value: Operator, data: number): string => { + + const currentIteration = getRangeErrorMessagesRecursively(value, data); + + // build complete error message from found parts + let errorMessage = ''; + if (currentIteration.length > 1) { + + currentIteration.forEach((element, index) => { + if (index === 0) { + errorMessage = createStartMessage(element); + } else if (index === currentIteration.length - 1) { + errorMessage += ` ${element}${errorMessageEnd}`; + } else { + errorMessage += `, ${element}`; + } + }); + } else if (currentIteration.length == 1) { + errorMessage = `${createStartMessage(currentIteration[0])}${errorMessageEnd}`; + } + + return errorMessage; +}; + +export const checkRange = (element: ViewElementNumber | ViewElementString, data: number): string => { + const number = data; + + let expression = undefined; + + if (isViewElementString(element)) { + expression = element.length; + + errorMessageStart = rangeErrorStartString; + errorMessageMiddleMaxPart = rangeErrorInnerMaxTextString; + errorMessageMiddleMinPart = rangeErrorInnerMinTextString; + errorMessageEnd = rangeErrorEndTextString; + + } else if (isViewElementNumber(element)) { + expression = element.range; + + errorMessageStart = rangeErrorStartNumber; + errorMessageMiddleMaxPart = rangeErrorInnerMaxTextNumber; + errorMessageMiddleMinPart = rangeErrorInnerMinTextNumber; + errorMessageEnd = rangeErrorEndTextNumber; + } + + if (expression) { + if (isYangOperator(expression)) { + + const errorMessage = getRangeErrorMessages(expression, data); + return errorMessage; + + } else + if (isYangRange(expression)) { + + if (!isNaN(expression.min)) { + if (number < expression.min) { + return `${errorMessageStart} ${errorMessageMiddleMinPart} ${expression.min}${errorMessageEnd}`; + } + } + + if (!isNaN(expression.max)) { + if (number > expression.max) { + return `${errorMessageStart} ${errorMessageMiddleMaxPart} ${expression.max}${errorMessageEnd}`; + } + } + } + } + + return ''; +}; + +const getRegexRecursively = (value: Operator, data: string): boolean[] => { + let currentItteration: boolean[] = []; + for (let i = 0; i < value.arguments.length; i++) { + const element = value.arguments[i]; + if (isRegExp(element)) { + // if regex is found, add it to list + currentItteration.push(element.test(data)); + } else if (isRegExpOperator(element)) { + //if RegexExpression is found, try to get regex from it + currentItteration = currentItteration.concat(getRegexRecursively(element, data)); + } + } + + if (value.operation === 'OR') { + // if one is true, all are true, all found items can be discarded + let result = currentItteration.find(element => element); + if (result) { + return []; + } + } + return currentItteration; +}; + +const isPatternValid = (value: Operator, data: string): boolean => { + // get all regex + const result = getRegexRecursively(value, data); + + if (value.operation === 'AND') { + // if AND operation is executed... + // no element can be false + const check = result.find(element => element !== true); + if (check) + return false; + else + return true; + } else { + // if OR operation is executed... + // ... just one element must be true + const check = result.find(element => element === true); + if (check) + return true; + else + return false; + + } +}; + +export const checkPattern = (expression: RegExp | Operator | undefined, data: string): validated => { + + if (expression) { + if (isRegExp(expression)) { + const isValid = expression.test(data); + if (!isValid) + return { isValid: isValid, error: 'The input is in a wrong format.' }; + + } else if (isRegExpOperator(expression)) { + const result = isPatternValid(expression, data); + + if (!result) { + return { isValid: false, error: 'The input is in a wrong format.' }; + } + } + } + + return { isValid: true }; +}; + + + + diff --git a/sdnr/wt/odlux/apps/configurationApp/src/utilities/viewEngineHelper.ts b/sdnr/wt/odlux/apps/configurationApp/src/utilities/viewEngineHelper.ts new file mode 100644 index 000000000..ad34c83b9 --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/utilities/viewEngineHelper.ts @@ -0,0 +1,324 @@ +import { storeService } from '../../../../framework/src/services/storeService'; +import { WhenAST, WhenTokenType } from '../yang/whenParser'; + +import { + ViewSpecification, + ViewElement, + isViewElementReference, + isViewElementList, + isViewElementObjectOrList, + isViewElementRpc, + isViewElementChoice, + ViewElementChoiceCase, +} from '../models/uiModels'; + +import { Module } from '../models/yang'; + +import { restService } from '../services/restServices'; + +export type HttpResult = { + status: number; + message?: string | undefined; + data: { + [key: string]: any; + } | null | undefined; +}; + +export const checkResponseCode = (restResult: HttpResult) =>{ + //403 gets handled by the framework from now on + return restResult.status !== 403 && ( restResult.status < 200 || restResult.status > 299); +}; + +export const resolveVPath = (current: string, vPath: string): string => { + if (vPath.startsWith('/')) { + return vPath; + } + const parts = current.split('/'); + const vPathParts = vPath.split('/'); + for (const part of vPathParts) { + if (part === '.') { + continue; + } else if (part === '..') { + parts.pop(); + } else { + parts.push(part); + } + } + return parts.join('/'); +}; + +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 derivedFrom = (vPath: string, when: WhenAST, viewData: any, includeSelf = false) => { + if (when.args?.length !== 2) { + throw new Error('derived-from or derived-from-or-self requires 2 arguments.'); + } + const [arg1, arg2] = when.args; + if (arg1.type !== WhenTokenType.IDENTIFIER || arg2.type !== WhenTokenType.STRING) { + throw new Error('derived-from or derived-from-or-self requires first argument IDENTIFIER and second argument STRING.'); + } + + if (!storeService.applicationStore) { + throw new Error('storeService.applicationStore is not defined.'); + } + + const pathParts = splitVPath(arg1.value as string || '', /(?:(?:([^\/\:]+):)?([^\/]+))/g); + const referenceValueParts = /(?:(?:([^\/\:]+):)?([^\/]+))/g.exec(arg2.value as string || ''); + + if (!pathParts || !referenceValueParts || pathParts.length === 0 || referenceValueParts.length === 0) { + throw new Error('derived-from or derived-from-or-self requires first argument PATH and second argument IDENTITY.'); + } + + if (pathParts[0][1]?.startsWith('..') || pathParts[0][1]?.startsWith('/')) { + throw new Error('derived-from or derived-from-or-self currently only supports relative paths.'); + } + + const { configuration: { deviceDescription: { modules } } } = storeService.applicationStore.state; + const dataValue = pathParts.reduce((acc, [ns, prop]) => { + if (prop === '.') { + return acc; + } + if (acc && prop) { + const moduleName = ns && (Object.values(modules).find((m: Module) => m.prefix === ns) || Object.values(modules).find((m: Module) => m.name === ns))?.name; + return (moduleName) ? acc[`${moduleName}:${prop}`] || acc[prop] : acc[prop]; + } + return undefined; + }, viewData); + + let dataValueParts = dataValue && /(?:(?:([^\/\:]+):)?([^\/]+))/g.exec(dataValue); + if (!dataValueParts || dataValueParts.length < 2) { + throw new Error(`derived-from or derived-from-or-self value referenced by first argument [${arg1.value}] not found.`); + } + let [, dataValueNs, dataValueProp] = dataValueParts; + let dataValueModule: Module = dataValueNs && (Object.values(modules).find((m: Module) => m.name === dataValueNs)); + let dataValueIdentity = dataValueModule && dataValueModule.identities && (Object.values(dataValueModule.identities).find((i) => i.label === dataValueProp)); + + if (!dataValueIdentity) { + throw new Error(`derived-from or derived-from-or-self identity [${dataValue}] referenced by first argument [${arg1.value}] not found.`); + } + + const [, referenceValueNs, referenceValueProp] = referenceValueParts; + const referenceValueModule = referenceValueNs && (Object.values(modules).find((m: Module) => m.prefix === referenceValueNs)); + const referenceValueIdentity = referenceValueModule && referenceValueModule.identities && (Object.values(referenceValueModule.identities).find((i) => i.label === referenceValueProp)); + + if (!referenceValueIdentity) { + throw new Error(`derived-from or derived-from-or-self identity [${arg2.value}] referenced by second argument not found.`); + } + + let result = includeSelf && (referenceValueIdentity === dataValueIdentity); + while (dataValueIdentity && dataValueIdentity.base && !result) { + dataValueParts = dataValue && /(?:(?:([^\/\:]+):)?([^\/]+))/g.exec(dataValueIdentity.base); + const [, innerDataValueNs, innerDataValueProp] = dataValueParts; + dataValueModule = innerDataValueNs && (Object.values(modules).find((m: Module) => m.prefix === innerDataValueNs)) || dataValueModule; + dataValueIdentity = dataValueModule && dataValueModule.identities && (Object.values(dataValueModule.identities).find((i) => i.label === innerDataValueProp)) ; + result = (referenceValueIdentity === dataValueIdentity); + } + + return result; +}; + +const evaluateWhen = async (vPath: string, when: WhenAST, viewData: any): Promise => { + switch (when.type) { + case WhenTokenType.FUNCTION: + switch (when.name) { + case 'derived-from-or-self': + return derivedFrom(vPath, when, viewData, true); + case 'derived-from': + return derivedFrom(vPath, when, viewData, false); + default: + throw new Error(`Unknown function ${when.name}`); + } + case WhenTokenType.AND: + return !when.left || !when.right || (await evaluateWhen(vPath, when.left, viewData) && await evaluateWhen(vPath, when.right, viewData)); + case WhenTokenType.OR: + return !when.left || !when.right || (await evaluateWhen(vPath, when.left, viewData) || await evaluateWhen(vPath, when.right, viewData)); + case WhenTokenType.NOT: + return !when.right || ! await evaluateWhen(vPath, when.right, viewData); + case WhenTokenType.EXPRESSION: + return !(when.value && typeof when.value !== 'string') || await evaluateWhen(vPath, when.value, viewData); + } + return true; +}; + +export const getReferencedDataList = async (refPath: string, dataPath: string, modules: { [name: string]: Module }, views: ViewSpecification[]) => { + const pathParts = splitVPath(refPath, /(?:(?:([^\/\:]+):)?([^\/]+))/g); // 1 = opt: namespace / 2 = property + const defaultNS = pathParts[0][0]; + let referencedModule = modules[defaultNS]; + + 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}]. View element 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 || checkResponseCode(restResult)) { + 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[`${defaultNS}:${dataMember!}`]; + if (dataRaw === undefined) { + 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 || checkResponseCode(restResult)) { + 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[`${defaultNS}:${dataMember!}`]; + if (dataRaw === undefined) { + 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; +}; + +export const resolveViewDescription = (defaultNS: string | null, vPath: string, view: ViewSpecification): ViewSpecification =>{ + + // resolve all references. + view = { ...view }; + view.elements = Object.keys(view.elements).reduce<{ [name: string]: ViewElement }>((acc, cur) => { + const resolveHistory : ViewElement[] = []; + let elm = view.elements[cur]; + const key = defaultNS && cur.replace(new RegExp(`^${defaultNS}:`, 'i'), '') || cur; + while (isViewElementReference(elm)) { + const result = (elm.ref(vPath)); + if (result) { + const [referencedElement, referencedPath] = result; + if (resolveHistory.some(hist => hist === referencedElement)) { + console.error(`Circle reference found at: ${vPath}`, resolveHistory); + break; + } + elm = referencedElement; + vPath = referencedPath; + resolveHistory.push(elm); + } + } + + acc[key] = { ...elm, id: key }; + + return acc; + }, {}); + return view; +}; + +export const flattenViewElements = (defaultNS: string | null, parentPath: string, elements: { [name: string]: ViewElement }, views: ViewSpecification[], currentPath: string ): { [name: string]: ViewElement } => { + if (!elements) return {}; + return Object.keys(elements).reduce<{ [name: string]: ViewElement }>((acc, cur) => { + const elm = elements[cur]; + + // remove the default namespace, and only the default namespace, sine it seems that this is also not in the restconf response + const elmKey = defaultNS && elm.id.replace(new RegExp(`^${defaultNS}:`, 'i'), '') || elm.id; + const key = parentPath ? `${parentPath}.${elmKey}` : elmKey; + + if (isViewElementRpc(elm)) { + console.warn(`Flatten of RFC not supported ! [${currentPath}][${elm.label}]`); + return acc; + } else if (isViewElementObjectOrList(elm)) { + const view = views[+elm.viewId]; + const inner = view && flattenViewElements(defaultNS, key, view.elements, views, `${currentPath}/${view.name}`); + if (inner) { + Object.keys(inner).forEach(k => (acc[k] = inner[k])); + } + } else if (isViewElementChoice(elm)) { + acc[key] = { + ...elm, + id: key, + cases: Object.keys(elm.cases).reduce<{ [name: string]: ViewElementChoiceCase }>((accCases, curCases) => { + const caseElement = elm.cases[curCases]; + accCases[curCases] = { + ...caseElement, + // Hint: do not use key it contains elmKey, which shell be omitted for cases. + elements: flattenViewElements(defaultNS, /*key*/ parentPath, caseElement.elements, views, `${currentPath}/${elm.label}`), + }; + return accCases; + }, {}), + }; + } else { + acc[key] = { + ...elm, + id: key, + }; + } + return acc; + }, {}); +}; + +export const filterViewElements = async (vPath: string, viewData: any, viewSpecification: ViewSpecification) => { + // filter elements of viewSpecification by evaluating when property + return Object.keys(viewSpecification.elements).reduce(async (accPromise, cur) => { + const acc = await accPromise; + const elm = viewSpecification.elements[cur]; + if (!elm.when || await evaluateWhen(vPath || '', elm.when, viewData).catch((ex) => { + console.warn(`Error evaluating when clause at: ${viewSpecification.name} for element: ${cur}`, ex); + return true; + })) { + acc.elements[cur] = elm; + } + return acc; + }, Promise.resolve({ ...viewSpecification, elements: {} as { [key: string]: ViewElement } })); +}; \ 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 index 0e2ddb395..0f143d818 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/views/configurationApplication.tsx @@ -25,17 +25,41 @@ import { WithStyles } from '@mui/styles'; import withStyles from '@mui/styles/withStyles'; import createStyles from '@mui/styles/createStyles'; -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 { useConfirm } from 'material-ui-confirm'; + +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 { renderObject } from '../../../../framework/src/components/objectDump'; import { DisplayModeType } from '../handlers/viewDescriptionHandler'; -import { SetSelectedValue, splitVPath, updateDataActionAsyncCreator, updateViewActionAsyncCreator, removeElementActionAsyncCreator, executeRpcActionAsyncCreator } from "../actions/deviceActions"; -import { ViewSpecification, isViewElementString, isViewElementNumber, isViewElementBoolean, isViewElementObjectOrList, isViewElementSelection, isViewElementChoise, ViewElement, ViewElementChoise, isViewElementUnion, isViewElementRpc, ViewElementRpc, isViewElementEmpty, isViewElementDate } from "../models/uiModels"; - -import { getAccessPolicyByUrl } from "../../../../framework/src/services/restService"; +import { + SetSelectedValue, + updateDataActionAsyncCreator, + updateViewActionAsyncCreator, + removeElementActionAsyncCreator, + executeRpcActionAsyncCreator, +} from '../actions/deviceActions'; + +import { + ViewElement, + ViewSpecification, + ViewElementChoice, + ViewElementRpc, + isViewElementString, + isViewElementNumber, + isViewElementBoolean, + isViewElementObjectOrList, + isViewElementSelection, + isViewElementChoice, + isViewElementUnion, + isViewElementRpc, + isViewElementEmpty, + isViewElementDate, +} from '../models/uiModels'; + +import { getAccessPolicyByUrl } from '../../../../framework/src/services/restService'; import Fab from '@mui/material/Fab'; import AddIcon from '@mui/icons-material/Add'; @@ -44,23 +68,22 @@ import ArrowBack from '@mui/icons-material/ArrowBack'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; import SaveIcon from '@mui/icons-material/Save'; import EditIcon from '@mui/icons-material/Edit'; -import Tooltip from "@mui/material/Tooltip"; -import FormControl from "@mui/material/FormControl"; -import IconButton from "@mui/material/IconButton"; - -import InputLabel from "@mui/material/InputLabel"; -import Select from "@mui/material/Select"; -import MenuItem from "@mui/material/MenuItem"; -import Breadcrumbs from "@mui/material/Breadcrumbs"; +import Tooltip from '@mui/material/Tooltip'; +import FormControl from '@mui/material/FormControl'; +import IconButton from '@mui/material/IconButton'; + +import InputLabel from '@mui/material/InputLabel'; +import Select from '@mui/material/Select'; +import MenuItem from '@mui/material/MenuItem'; +import Breadcrumbs from '@mui/material/Breadcrumbs'; import Button from '@mui/material/Button'; -import Link from "@mui/material/Link"; +import Link from '@mui/material/Link'; import Accordion from '@mui/material/Accordion'; import AccordionSummary from '@mui/material/AccordionSummary'; import AccordionDetails from '@mui/material/AccordionDetails'; import Typography from '@mui/material/Typography'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; - import { BaseProps } from '../components/baseProps'; import { UIElementReference } from '../components/uiElementReference'; import { UiElementNumber } from '../components/uiElementNumber'; @@ -70,44 +93,43 @@ import { UiElementSelection } from '../components/uiElementSelection'; import { UIElementUnion } from '../components/uiElementUnion'; import { UiElementLeafList } from '../components/uiElementLeafList'; -import { useConfirm } from 'material-ui-confirm'; -import restService from '../services/restServices'; +import { splitVPath } from '../utilities/viewEngineHelper'; const styles = (theme: Theme) => createStyles({ header: { - "display": "flex", - "justifyContent": "space-between", + 'display': 'flex', + 'justifyContent': 'space-between', }, leftButton: { - "justifyContent": "left" + 'justifyContent': 'left', }, outer: { - "flex": "1", - "height": "100%", - "display": "flex", - "alignItems": "center", - "justifyContent": "center", + 'flex': '1', + 'height': '100%', + 'display': 'flex', + 'alignItems': 'center', + 'justifyContent': 'center', }, inner: { }, container: { - "height": "100%", - "display": "flex", - "flexDirection": "column", + 'height': '100%', + 'display': 'flex', + 'flexDirection': 'column', }, - "icon": { - "marginRight": theme.spacing(0.5), - "width": 20, - "height": 20, + 'icon': { + 'marginRight': theme.spacing(0.5), + 'width': 20, + 'height': 20, }, - "fab": { - "margin": theme.spacing(1), + 'fab': { + 'margin': theme.spacing(1), }, button: { margin: 0, - padding: "6px 6px", - minWidth: 'unset' + padding: '6px 6px', + minWidth: 'unset', }, readOnly: { '& label.Mui-focused': { @@ -129,29 +151,29 @@ const styles = (theme: Theme) => createStyles({ }, }, uiView: { - overflowY: "auto", + overflowY: 'auto', }, section: { - padding: "15px", + padding: '15px', borderBottom: `2px solid ${theme.palette.divider}`, }, viewElements: { - width: 485, marginLeft: 20, marginRight: 20 + width: 485, marginLeft: 20, marginRight: 20, }, verificationElements: { - width: 485, marginLeft: 20, marginRight: 20 + width: 485, marginLeft: 20, marginRight: 20, }, heading: { fontSize: theme.typography.pxToRem(15), fontWeight: theme.typography.fontWeightRegular, }, moduleCollection: { - marginTop: "16px", - overflow: "auto", + marginTop: '16px', + overflow: 'auto', }, objectReult: { - overflow: "auto" - } + overflow: 'auto', + }, }); const mapProps = (state: IApplicationStoreState) => ({ @@ -183,10 +205,10 @@ type ConfigurationApplicationComponentState = { editMode: boolean; canEdit: boolean; viewData: { [key: string]: any } | null; - choises: { [path: string]: { selectedCase: string, data: { [property: string]: any } } }; -} + choices: { [path: string]: { selectedCase: string; data: { [property: string]: any } } }; +}; -type GetStatelessComponentProps = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any +type GetStatelessComponentProps = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any; const AccordionSummaryExt: React.FC> = (props) => { const [disabled, setDisabled] = useState(true); const onMouseDown = (ev: React.MouseEvent) => { @@ -202,7 +224,7 @@ const AccordionSummaryExt: React.FC { /** @@ -216,17 +238,17 @@ class ConfigurationApplicationComponent extends React.Component { + private static getChoicesFromElements = (elements: { [name: string]: ViewElement }, viewData: any) => { return Object.keys(elements).reduce((acc, cur) => { const elm = elements[cur]; - if (isViewElementChoise(elm)) { + if (isViewElementChoice(elm)) { const caseKeys = Object.keys(elm.cases); - // find the right case for this choise, use the first one with data, at least use index 0 + // find the right case for this choice, use the first one with data, at least use index 0 const selectedCase = caseKeys.find(key => { const caseElm = elm.cases[key]; return Object.keys(caseElm.elements).some(caseElmKey => { @@ -255,26 +277,26 @@ class ConfigurationApplicationComponent extends React.Component { this.props.history.push(`${this.props.match.url}${path}`); - } + }; private changeValueFor = (property: string, value: any) => { this.setState({ viewData: { ...this.state.viewData, - [property]: value - } + [property]: value, + }, }); - } + }; private collectData = (elements: { [name: string]: ViewElement }) => { - // ensure only active choises will be contained + // ensure only active choices will be contained const viewData: { [key: string]: any } = { ...this.state.viewData }; - const choiseKeys = Object.keys(elements).filter(elmKey => isViewElementChoise(elements[elmKey])); - const elementsToRemove = choiseKeys.reduce((acc, curChoiceKey) => { - const currentChoice = elements[curChoiceKey] as ViewElementChoise; - const selectedCase = this.state.choises[curChoiceKey].selectedCase; + const choiceKeys = Object.keys(elements).filter(elmKey => isViewElementChoice(elements[elmKey])); + const elementsToRemove = choiceKeys.reduce((acc, curChoiceKey) => { + const currentChoice = elements[curChoiceKey] as ViewElementChoice; + const selectedCase = this.state.choices[curChoiceKey].selectedCase; Object.keys(currentChoice.cases).forEach(caseKey => { const caseElements = currentChoice.cases[caseKey].elements; if (caseKey === selectedCase) { @@ -311,7 +333,7 @@ class ConfigurationApplicationComponent extends React.Component { acc.push(caseElements[caseElementKey]); }); @@ -325,17 +347,17 @@ class ConfigurationApplicationComponent extends React.Component { const policy = getAccessPolicyByUrl(`${dataPath}/${element.id}`); return !(policy.GET && policy.POST); - } + }; private isPolicyModuleForbidden = (moduleName: string, dataPath: string): boolean => { const policy = getAccessPolicyByUrl(`${dataPath}/${moduleName}`); return !(policy.GET && policy.POST); - } + }; private getEditorForViewElement = (uiElement: ViewElement): (null | React.ComponentType>) => { if (isViewElementEmpty(uiElement)) { @@ -353,12 +375,12 @@ class ConfigurationApplicationComponent extends React.Component { const isKey = (uiElement.label === keyProperty); @@ -377,7 +399,7 @@ class ConfigurationApplicationComponent extends React.Component { this.changeValueFor(uiElement.id, e) }} + onChange={(e) => { this.changeValueFor(uiElement.id, e); }} getEditorForViewElement={this.getEditorForViewElement} />; } else { @@ -391,7 +413,7 @@ class ConfigurationApplicationComponent extends React.Component { this.changeValueFor(uiElement.id, e) }} + onChange={(e) => { this.changeValueFor(uiElement.id, e); }} />) : null; } @@ -418,14 +440,14 @@ class ConfigurationApplicationComponent extends React.Component { + private renderUIChoice = (uiElement: ViewElementChoice, viewData: { [key: string]: any }, keyProperty: string | undefined, editMode: boolean, isNew: boolean) => { const isKey = (uiElement.label === keyProperty); - const currentChoise = this.state.choises[uiElement.id]; - const currentCase = currentChoise && uiElement.cases[currentChoise.selectedCase]; + const currentChoice = this.state.choices[uiElement.id]; + const currentCase = currentChoice && uiElement.cases[currentChoice.selectedCase]; const canEdit = editMode && (isNew || (uiElement.config && !isKey)); - if (isViewElementChoise(uiElement)) { + if (isViewElementChoice(uiElement)) { const subElements = currentCase?.elements; return ( <> @@ -435,14 +457,14 @@ class ConfigurationApplicationComponent extends React.Component { - if (currentChoise.selectedCase === e.target.value) { + if (currentChoice.selectedCase === e.target.value) { return; // nothing changed } - this.setState({ choises: { ...this.state.choises, [uiElement.id]: { ...this.state.choises[uiElement.id], selectedCase: e.target.value as string } } }); + this.setState({ choices: { ...this.state.choices, [uiElement.id]: { ...this.state.choices[uiElement.id], selectedCase: e.target.value as string } } }); }} readOnly={!canEdit} disabled={editMode && !canEdit} - value={this.state.choises[uiElement.id].selectedCase} + value={this.state.choices[uiElement.id].selectedCase} inputProps={{ name: uiElement.id, id: `select-${uiElement.id}`, @@ -452,7 +474,7 @@ class ConfigurationApplicationComponent extends React.Component { const caseElm = uiElement.cases[caseKey]; return ( -
    {caseElm.label}
    +
    {caseElm.label}
    ); }) } @@ -463,13 +485,13 @@ class ConfigurationApplicationComponent extends React.ComponentInvalid Choise + :

    Invalid Choice

    } ); } else { - if (process.env.NODE_ENV !== "production") { - console.error(`Unknown type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`) + if (process.env.NODE_ENV !== 'production') { + console.error(`Unknown type - ${(uiElement as any).uiType} in ${(uiElement as any).id}.`); } return null; } @@ -478,8 +500,6 @@ class ConfigurationApplicationComponent extends React.Component { const { classes } = this.props; - - const orderFunc = (vsA: ViewElement, vsB: ViewElement) => { if (keyProperty) { // if (vsA.label === vsB.label) return 0; @@ -497,15 +517,15 @@ class ConfigurationApplicationComponent extends React.Component {sections.references.map(element => ( - { this.navigate(`/${elm.id}`) }} /> + { this.navigate(`/${elm.id}`); }} /> ))} ) : null } - {sections.choises.length > 0 + {sections.choices.length > 0 ? (
    - {sections.choises.map(element => this.renderUIChoise(element, viewData, keyProperty, editMode, isNew))} + {sections.choices.map(element => this.renderUIChoice(element, viewData, keyProperty, editMode, isNew))}
    ) : null } @@ -539,7 +559,7 @@ class ConfigurationApplicationComponent extends React.Component {sections.rpcs.map(element => ( - { this.navigate(`/${elm.id}`) }} /> + { this.navigate(`/${elm.id}`); }} /> ))} ) : null @@ -550,6 +570,7 @@ class ConfigurationApplicationComponent extends React.Component { const { classes } = this.props; + // group by module name const modules = Object.keys(viewSpecification.elements).reduce<{ [key: string]: ViewSpecification }>((acc, cur) => { const elm = viewSpecification.elements[cur]; @@ -565,6 +586,7 @@ class ConfigurationApplicationComponent extends React.Component { const moduleView = modules[key]; + return ( } aria-controls={`content-${key}`} id={`header-${key}`} disabled={this.isPolicyModuleForbidden(`${key}:`, dataPath)} > @@ -584,8 +606,8 @@ class ConfigurationApplicationComponent extends React.Component { - navigate("[]"); // empty key means new element + navigate('[]'); // empty key means new element }, disabled: !config, }; @@ -615,11 +637,11 @@ class ConfigurationApplicationComponent extends React.Component void }> = (props) => { + const DeleteIconWithConfirmation: React.FC<{ disabled?: boolean; rowData: { [key: string]: any }; onReload: () => void }> = (props) => { const confirm = useConfirm(); return ( - + { e.stopPropagation(); e.preventDefault(); - confirm({ title: "Do you really want to delete this element ?", description: "This action is permanent!", confirmationButtonProps: { color: "secondary" }, cancellationButtonProps: { color:"inherit" } }) - .then(() => { - let keyId = ""; - if (listKeyProperty && listKeyProperty.split(" ").length > 1) { - keyId += listKeyProperty.split(" ").map(id => props.rowData[id]).join(","); + confirm({ title: 'Do you really want to delete this element ?', description: 'This action is permanent!', confirmationButtonProps: { color: 'secondary' }, cancellationButtonProps: { color:'inherit' } }) + .then(() => { + let keyId = ''; + if (listKeyProperty && listKeyProperty.split(' ').length > 1) { + keyId += listKeyProperty.split(' ').map(id => props.rowData[id]).join(','); } else { - keyId = props.rowData[listKeyProperty]; - } - return removeElement(`${this.props.vPath}[${keyId}]`) + keyId = props.rowData[listKeyProperty]; + } + return removeElement(`${this.props.vPath}[${keyId}]`); }).then(props.onReload); }} size="large"> @@ -643,44 +665,46 @@ class ConfigurationApplicationComponent extends React.Component ); - } + }; return ( []>((acc, cur) => { const elm = listElements[cur]; - if (elm.uiType !== "object" && listData.every(entry => entry[elm.label] != null)) { + if (elm.uiType !== 'object' && listData.every(entry => entry[elm.label] != null)) { if (elm.label !== listKeyProperty) { - acc.push(elm.uiType === "boolean" + acc.push(elm.uiType === 'boolean' ? { property: elm.label, type: ColumnType.boolean } - : elm.uiType === "date" + : elm.uiType === 'date' ? { property: elm.label, type: ColumnType.date } - : { property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text }); + : { property: elm.label, type: elm.uiType === 'number' ? ColumnType.numeric : ColumnType.text }); } else { - acc.unshift(elm.uiType === "boolean" + acc.unshift(elm.uiType === 'boolean' ? { property: elm.label, type: ColumnType.boolean } - : elm.uiType === "date" + : elm.uiType === 'date' ? { property: elm.label, type: ColumnType.date } - : { property: elm.label, type: elm.uiType === "number" ? ColumnType.numeric : ColumnType.text }); + : { property: elm.label, type: elm.uiType === 'number' ? ColumnType.numeric : ColumnType.text }); } } return acc; }, []).concat([{ - property: "Actions", disableFilter: true, disableSorting: true, type: ColumnType.custom, customControl: (({ rowData }) => { + property: 'Actions', disableFilter: true, disableSorting: true, type: ColumnType.custom, customControl: (({ rowData }) => { return ( this.props.vPath && this.props.reloadView(this.props.vPath)} /> ); - }) + }), }]) } onHandleClick={(ev, row) => { ev.preventDefault(); - let keyId = "" - if (listKeyProperty && listKeyProperty.split(" ").length > 1) { - keyId += listKeyProperty.split(" ").map(id => row[id]).join(","); + let keyId = ''; + if (listKeyProperty && listKeyProperty.split(' ').length > 1) { + keyId += listKeyProperty.split(' ').map(id => row[id]).join(','); } else { keyId = row[listKeyProperty]; } - listKeyProperty && navigate(`[${encodeURIComponent(keyId)}]`); // Do not navigate without key. + if (listKeyProperty) { + navigate(`[${encodeURIComponent(keyId)}]`); // Do not navigate without key. + } }} > ); } @@ -704,17 +728,17 @@ class ConfigurationApplicationComponent extends React.Component { const elm = inputViewSpecification.elements[cur]; if (isViewElementObjectOrList(elm)) { - console.error("Object should not appear in RPC view !"); - } else if (isViewElementChoise(elm)) { - acc.choises.push(elm); + console.error('Object should not appear in RPC view !'); + } else if (isViewElementChoice(elm)) { + acc.choices.push(elm); } else if (isViewElementRpc(elm)) { - console.error("RPC should not appear in RPC view !"); + console.error('RPC should not appear in RPC view !'); } else { acc.elements.push(elm); } return acc; - }, { elements: [] as ViewElement[], references: [] as ViewElement[], choises: [] as ViewElementChoise[], rpcs: [] as ViewElementRpc[] }) - || { elements: [] as ViewElement[], references: [] as ViewElement[], choises: [] as ViewElementChoise[], rpcs: [] as ViewElementRpc[] }; + }, { elements: [] as ViewElement[], references: [] as ViewElement[], choices: [] as ViewElementChoice[], rpcs: [] as ViewElementRpc[] }) + || { elements: [] as ViewElement[], references: [] as ViewElement[], choices: [] as ViewElementChoice[], rpcs: [] as ViewElementRpc[] }; sections.elements = sections.elements.sort(orderFunc); @@ -728,10 +752,10 @@ class ConfigurationApplicationComponent extends React.Component ) : null } - { sections.choises.length > 0 + { sections.choices.length > 0 ? (
    - {sections.choises.map(element => this.renderUIChoise(element, inputViewData, keyProperty, editMode, isNew))} + {sections.choices.map(element => this.renderUIChoice(element, inputViewData, keyProperty, editMode, isNew))}
    ) : null } @@ -747,13 +771,13 @@ class ConfigurationApplicationComponent extends React.Component ); - }; + } private renderBreadCrumps() { const { editMode } = this.state; const { displaySpecification, vPath, nodeId } = this.props; const pathParts = splitVPath(vPath!, /(?:([^\/\["]+)(?:\[([^\]]*)\])?)/g); // 1 = property / 2 = optional key - let lastPath = `/configuration`; + let lastPath = '/configuration'; let basePath = `/configuration/${nodeId}`; return (
    @@ -774,7 +798,7 @@ class ConfigurationApplicationComponent extends React.Component { const path = `${basePath}/${prop}`; const keyPath = key && `${basePath}/${prop}[${key}]`; - const propTitle = prop.replace(/^[^:]+:/, ""); + const propTitle = prop.replace(/^[^:]+:/, ''); const ret = ( ) => { ev.preventDefault(); this.props.history.push(keyPath); - }}>{`[${key && key.replace(/\%2C/g, ",")}]`} || null - } + }}>{`[${key && key.replace(/\%2C/g, ',')}]`} || null + } ); lastPath = basePath; @@ -802,7 +826,9 @@ class ConfigurationApplicationComponent extends React.Component {this.state.editMode && ( { - this.props.vPath && (await this.props.reloadView(this.props.vPath)); + if (this.props.vPath) { + await this.props.reloadView(this.props.vPath); + } this.setState({ editMode: false }); }} > ) || null} @@ -810,7 +836,7 @@ class ConfigurationApplicationComponent extends React.Component { if (this.state.editMode) { - // ensure only active choises will be contained + // ensure only active choices will be contained const resultingViewData = this.collectData(displaySpecification.viewSpecification.elements); this.props.onUpdateData(this.props.vPath!, resultingViewData); } @@ -830,7 +856,7 @@ class ConfigurationApplicationComponent extends React.Component[]>((acc, cur) => { const elm = listSpecification.elements[cur]; - if (elm.uiType !== "object" && listData.every(entry => entry[elm.label] != null)) { + 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 }); + 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 }); + acc.unshift({ property: elm.label, type: elm.uiType === 'number' ? ColumnType.numeric : ColumnType.text }); } } return acc; diff --git a/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx b/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx index 1a1008dad..e96f40d61 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx +++ b/sdnr/wt/odlux/apps/configurationApp/src/views/networkElementSelector.tsx @@ -16,15 +16,15 @@ * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import 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 { 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 { NetworkElementConnection } from "../models/networkElementConnection"; +import { NetworkElementConnection } from '../models/networkElementConnection'; +import { createConnectedNetworkElementsProperties, createConnectedNetworkElementsActions } from '../../../configurationApp/src/handlers/connectedNetworkElementsHandler'; const mapProps = (state: IApplicationStoreState) => ({ @@ -47,20 +47,20 @@ class NetworkElementSelectorComponent extends React.Component { this.props.history.push(`${this.props.match.path}/${row.nodeId}`) }} columns={[ - { property: "nodeId", title: "Node 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 }, + { this.props.history.push(`${this.props.match.path}/${row.nodeId}`); }} columns={[ + { property: 'nodeId', title: 'Node 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 > ); diff --git a/sdnr/wt/odlux/apps/configurationApp/src/yang/whenParser.ts b/sdnr/wt/odlux/apps/configurationApp/src/yang/whenParser.ts new file mode 100644 index 000000000..fa2968c9c --- /dev/null +++ b/sdnr/wt/odlux/apps/configurationApp/src/yang/whenParser.ts @@ -0,0 +1,235 @@ +enum WhenTokenType { + AND = 'AND', + OR = 'OR', + NOT = 'NOT', + EQUALS = 'EQUALS', + COMMA = 'COMMA', + STRING = 'STRING', + FUNCTION = 'FUNCTION', + IDENTIFIER = 'IDENTIFIER', + OPEN_PAREN = 'OPEN_PAREN', + CLOSE_PAREN = 'CLOSE_PAREN', + EXPRESSION = 'EXPRESSION', +} + +type Token = { + type: WhenTokenType; + value: string; +}; + +const isAlpha = (char: string) => /[a-z]/i.test(char); + +const isAlphaNumeric = (char: string) => /[A-Za-z0-9_\-/:\.]/i.test(char); + +const lex = (input: string) : Token[] => { + let tokens = [] as any[]; + let current = 0; + + while (current < input.length) { + let char = input[current]; + + if (char === ' ') { + current++; + continue; + } + + if (char === '(') { + tokens.push({ type: WhenTokenType.OPEN_PAREN, value: char }); + current++; + continue; + } + + if (char === ')') { + tokens.push({ type: WhenTokenType.CLOSE_PAREN, value: char }); + current++; + continue; + } + + if (char === '=') { + tokens.push({ type: WhenTokenType.EQUALS, value: char }); + current++; + continue; + } + + if (char === ',') { + tokens.push({ type: WhenTokenType.COMMA, value: char }); + current++; + continue; + } + + if (char === '\"' || char === '\'') { + let value = ''; + let start = current; + current++; + + while (current < input.length) { + let innerChar = input[current]; + if (innerChar === '\\') { + value += input[current] + input[current + 1]; + current += 2; + } else if (innerChar === input[start]) { + current++; + break; + } else { + value += innerChar; + current++; + } + } + + tokens.push({ type: WhenTokenType.STRING, value }); + continue; + } + + if (isAlpha(char)) { + let value = ''; + while (isAlpha(char)) { + value += char; + char = input[++current]; + } + + switch (value) { + case 'and': + tokens.push({ type: WhenTokenType.AND }); + break; + case 'or': + tokens.push({ type: WhenTokenType.OR }); + break; + case 'not': + tokens.push({ type: WhenTokenType.NOT }); + break; + case 'eq': + tokens.push({ type: WhenTokenType.EQUALS }); + break; + default: + while (isAlphaNumeric(char)) { + value += char; + char = input[++current]; + } + tokens.push({ type: WhenTokenType.IDENTIFIER, value }); + } + + continue; + } + if (isAlphaNumeric(char)) { + let value = ''; + while (isAlphaNumeric(char)) { + value += char; + char = input[++current]; + } + + tokens.push({ type: WhenTokenType.IDENTIFIER, value }); + continue; + } + throw new TypeError(`I don't know what this character is: ${char}`); + } + return tokens; +}; + +type WhenAST = { + type: WhenTokenType; + left?: WhenAST; + right?: WhenAST; + value?: string | WhenAST; + name?: string; + args?: WhenAST[]; +}; + +const precedence : { [index: string] : number } = { + 'EQUALS': 4, + 'NOT': 3, + 'AND': 2, + 'OR': 1, +}; + +const parseWhen = (whenExpression: string) => { + const tokens = lex(whenExpression); + let current = 0; + + const walk = (precedenceLevel = 0) : WhenAST => { + let token = tokens[current]; + let node: WhenAST | null = null; + + if (token.type === WhenTokenType.OPEN_PAREN) { + token = tokens[++current]; + let innerNode: WhenAST = { type: WhenTokenType.EXPRESSION, value: walk() }; + token = tokens[current]; + + while (token.type !== WhenTokenType.CLOSE_PAREN) { + innerNode = { + type: token.type, + value: token.value, + left: innerNode, + right: walk(), + }; + token = tokens[current]; + } + current++; + return innerNode; + } + + if (token.type === WhenTokenType.STRING ) { + current++; + node = { type: token.type, value: token.value }; + } + + if (token.type === WhenTokenType.NOT) { + token = tokens[++current]; + node = { type: WhenTokenType.NOT, value: token.value, right: walk() }; + } + + if (token.type === WhenTokenType.IDENTIFIER) { + const nextToken = tokens[current + 1]; + if (nextToken.type === WhenTokenType.OPEN_PAREN) { + let name = token.value; + token = tokens[++current]; + + let args = []; + token = tokens[++current]; + + while (token.type !== WhenTokenType.CLOSE_PAREN) { + if (token.type === WhenTokenType.COMMA) { + current++; + } else { + args.push(walk()); + } + token = tokens[current]; + } + + current++; + node = { type: WhenTokenType.FUNCTION, name, args }; + } else { + current++; + node = { type: WhenTokenType.IDENTIFIER, value: token.value }; + } + } + + if (!node) throw new TypeError('Unexpected token: ' + token.type); + + token = tokens[current]; + while (current < tokens.length && precedence[token.type] >= precedenceLevel) { + console.log(current, tokens[current], tokens[current].type, precedenceLevel, precedence[token.type]); + token = tokens[current]; + if (token.type === WhenTokenType.EQUALS || token.type === WhenTokenType.AND || token.type === WhenTokenType.OR) { + current++; + node = { + type: token.type, + left: node, + right: walk(precedence[token.type]), + }; + } else { + break; + } + } + + return node; + + }; + + return walk(); +}; + +export { + parseWhen, + WhenAST, + WhenTokenType, +}; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts index e8e636f9b..cc2520100 100644 --- a/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts +++ b/sdnr/wt/odlux/apps/configurationApp/src/yang/yangParser.ts @@ -1,3 +1,6 @@ +/* eslint-disable @typescript-eslint/no-loss-of-precision */ +/* eslint-disable @typescript-eslint/no-unused-expressions */ +/* eslint-disable @typescript-eslint/naming-convention */ /** * ============LICENSE_START======================================================================== * ONAP : ccsdk feature sdnr wt odlux @@ -15,14 +18,30 @@ * the License. * ============LICENSE_END========================================================================== */ -import { Token, Statement, Module, Identity, ModuleState } from "../models/yang"; +import { Token, Statement, Module, Identity, ModuleState } from '../models/yang'; import { - ViewSpecification, ViewElement, isViewElementObjectOrList, ViewElementBase, - isViewElementReference, ViewElementChoise, ViewElementBinary, ViewElementString, isViewElementString, - isViewElementNumber, ViewElementNumber, Expression, YangRange, ViewElementUnion, ViewElementRpc, isViewElementRpc, ResolveFunction, ViewElementDate -} from "../models/uiModels"; -import { yangService } from "../services/yangService"; - + Expression, + ViewElement, + ViewElementBase, + ViewSpecification, + ViewElementNumber, + ViewElementString, + ViewElementChoice, + ViewElementUnion, + ViewElementRpc, + isViewElementObjectOrList, + isViewElementNumber, + isViewElementString, + isViewElementRpc, + ResolveFunction, + YangRange, +} from '../models/uiModels'; +import { yangService } from '../services/yangService'; + +const LOGLEVEL = +(localStorage.getItem('log.odlux.app.configuration.yang.yangParser') || 0); + +import { LogLevel } from '../../../../framework/src/utilities/logLevel'; +import { parseWhen, WhenAST, WhenTokenType } from './whenParser'; export const splitVPath = (vPath: string, vPathParser: RegExp): RegExpMatchArray[] => { const pathParts: RegExpMatchArray[] = []; @@ -32,21 +51,22 @@ export const splitVPath = (vPath: string, vPathParser: RegExp): RegExpMatchArray if (partMatch) { pathParts.push(partMatch); } - } while (partMatch) + } while (partMatch); return pathParts; -} +}; class YangLexer { private pos: number = 0; - private buf: string = ""; + + private buf: string = ''; constructor(input: string) { this.pos = 0; this.buf = input; } - private _optable: { [key: string]: string } = { + private _opTable: { [key: string]: string } = { ';': 'SEMI', '{': 'L_BRACE', '}': 'R_BRACE', @@ -66,7 +86,7 @@ class YangLexer { private _isAlpha(char: string): boolean { return (char >= 'a' && char <= 'z') || - (char >= 'A' && char <= 'Z') + (char >= 'A' && char <= 'Z'); } private _isAlphanum(char: string): boolean { @@ -74,7 +94,7 @@ class YangLexer { char === '_' || char === '-' || char === '.'; } - private _skipNontokens() { + private _skipNonTokens() { while (this.pos < this.buf.length) { const char = this.buf.charAt(this.pos); if (this._isWhitespace(char)) { @@ -90,11 +110,11 @@ class YangLexer { let end_index = this.pos + 1; while (end_index < this.buf.length) { const char = this.buf.charAt(end_index); - if (char === "\\") { + if (char === '\\') { end_index += 2; continue; - }; - if (terminator === null && (this._isWhitespace(char) || this._optable[char] !== undefined) || char === terminator) { + } + if (terminator === null && (this._isWhitespace(char) || this._opTable[char] !== undefined) || char === terminator) { break; } end_index++; @@ -109,7 +129,7 @@ class YangLexer { name: 'STRING', value: this.buf.substring(start, end), start, - end + end, }; this.pos = terminator ? end + 1 : end; return tok; @@ -122,8 +142,8 @@ class YangLexer { ++endpos; } - let name = 'IDENTIFIER' - if (this.buf.charAt(endpos) === ":") { + let name = 'IDENTIFIER'; + if (this.buf.charAt(endpos) === ':') { name = 'IDENTIFIERREF'; ++endpos; while (endpos < this.buf.length && this._isAlphanum(this.buf.charAt(endpos))) { @@ -135,7 +155,7 @@ class YangLexer { name: name, value: this.buf.substring(this.pos, endpos), start: this.pos, - end: endpos + end: endpos, }; this.pos = endpos; @@ -153,7 +173,7 @@ class YangLexer { name: 'NUMBER', value: this.buf.substring(this.pos, endpos), start: this.pos, - end: endpos + end: endpos, }; this.pos = endpos; return tok; @@ -171,7 +191,7 @@ class YangLexer { 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) === "*"))) { + while (endpos < this.buf.length && !((this.buf.charAt(endpos) === '/' && this.buf.charAt(endpos - 1) === '*'))) { endpos++; } this.pos = endpos + 1; @@ -179,87 +199,87 @@ class YangLexer { public tokenize(): Token[] { const result: Token[] = []; - this._skipNontokens(); + this._skipNonTokens(); while (this.pos < this.buf.length) { const char = this.buf.charAt(this.pos); - const op = this._optable[char]; + 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(); + this._skipNonTokens(); const peekChar = this.buf.charAt(this.pos); - if (this._optable[peekChar] === undefined) { - result.push((peekChar !== "'" && peekChar !== '"') + if (this._opTable[peekChar] === undefined) { + result.push((peekChar !== '\'' && peekChar !== '"') ? this._processString(null) : this._processString(peekChar)); } - } else if (char === '/' && this.buf.charAt(this.pos + 1) === "/") { + } else if (char === '/' && this.buf.charAt(this.pos + 1) === '/') { this._processLineComment(); - } else if (char === '/' && this.buf.charAt(this.pos + 1) === "*") { + } else if (char === '/' && this.buf.charAt(this.pos + 1) === '*') { this._processBlockComment(); } else { - throw Error('Token error at ' + this.pos + " " + this.buf[this.pos]); + throw Error('Token error at ' + this.pos + ' ' + this.buf[this.pos]); } - this._skipNontokens(); + this._skipNonTokens(); } return result; } public tokenize2(): Statement { - let stack: Statement[] = [{ key: "ROOT", sub: [] }]; + let stack: Statement[] = [{ key: 'ROOT', sub: [] }]; let current: Statement | null = null; - this._skipNontokens(); + this._skipNonTokens(); while (this.pos < this.buf.length) { const char = this.buf.charAt(this.pos); - const op = this._optable[char]; + const op = this._opTable[char]; if (op !== undefined) { - if (op === "L_BRACE") { + if (op === 'L_BRACE') { current && stack.unshift(current); current = null; - } else if (op === "R_BRACE") { + } else if (op === 'R_BRACE') { current = stack.shift() || null; } this.pos++; - } else if (this._isAlpha(char) || char === "_") { + } else if (this._isAlpha(char) || char === '_') { const key = this._processIdentifier().value; - this._skipNontokens(); + this._skipNonTokens(); let peekChar = this.buf.charAt(this.pos); let arg = undefined; - if (this._optable[peekChar] === undefined) { - arg = (peekChar === '"' || peekChar === "'") + if (this._opTable[peekChar] === undefined) { + arg = (peekChar === '"' || peekChar === '\'') ? this._processString(peekChar).value : this._processString(null).value; } do { - this._skipNontokens(); + this._skipNonTokens(); peekChar = this.buf.charAt(this.pos); - if (peekChar !== "+") break; + if (peekChar !== '+') break; this.pos++; - this._skipNontokens(); + this._skipNonTokens(); peekChar = this.buf.charAt(this.pos); - arg += (peekChar === '"' || peekChar === "'") + 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) === "/") { + } else if (char === '/' && this.buf.charAt(this.pos + 1) === '/') { this._processLineComment(); - } else if (char === '/' && this.buf.charAt(this.pos + 1) === "*") { + } 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)); + throw Error('Token error at ' + this.pos + ' ' + this.buf.slice(this.pos - 10, this.pos + 10)); } - this._skipNontokens(); + this._skipNonTokens(); } - if (stack[0].key !== "ROOT" || !stack[0].sub![0]) { - throw new Error("Internal Perser Error"); + if (stack[0].key !== 'ROOT' || !stack[0].sub![0]) { + throw new Error('Internal Perser Error'); } return stack[0].sub![0]; } @@ -269,25 +289,33 @@ export class YangParser { private _groupingsToResolve: ViewSpecification[] = []; private _identityToResolve: (() => void)[] = []; + private _unionsToResolve: (() => void)[] = []; + private _modulesToResolve: (() => void)[] = []; private _modules: { [name: string]: Module } = {}; + private _views: ViewSpecification[] = [{ - id: "0", - name: "root", - language: "en-US", + id: '0', + name: 'root', + language: 'en-US', canEdit: false, config: true, - parentView: "0", - title: "root", + parentView: '0', + title: 'root', elements: {}, }]; - public static ResolveStack = Symbol("ResolveStack"); + public static ResolveStack = Symbol('ResolveStack'); + + constructor( + private nodeId: string, + private _capabilityRevisionMap: { [capability: string]: string } = {}, + private _unavailableCapabilities: { failureReason: string; capability: string }[] = [], + private _importOnlyModules: { name: string; revision: string }[] = [], + ) { - constructor(private _unavailableCapabilities: { failureReason: string; capability: string; }[] = [], private _importOnlyModules: { name: string; revision: string; }[] = [], private nodeId: string) { - } public get modules() { @@ -300,8 +328,12 @@ export class YangParser { public async addCapability(capability: string, version?: string, parentImportOnlyModule?: boolean) { // do not add twice - if (this._modules[capability]) { - // console.warn(`Skipped capability: ${capability} since already contained.` ); + const existingCapability = this._modules[capability]; + const latestVersionExisting = existingCapability && Object.keys(existingCapability.revisions).sort().reverse()[0]; + if ((latestVersionExisting && version) && (version <= latestVersionExisting)) { + if (LOGLEVEL == LogLevel.Warning) { + console.warn(`Skipped capability: ${capability}:${version || ''} since already contained.`); + } return; } @@ -310,14 +342,15 @@ export class YangParser { // // console.warn(`Skipped capability: ${capability} since it is marked as unavailable.` ); // return; // } + const data = await yangService.getCapability(capability, this.nodeId, version); if (!data) { - throw new Error(`Could not load yang file for ${capability}.`); + throw new Error(`Could not load yang file for ${capability}:${version || ''}.`); } const rootStatement = new YangLexer(data).tokenize2(); - if (rootStatement.key !== "module") { + if (rootStatement.key !== 'module') { throw new Error(`Root element of ${capability} is not a module.`); } if (rootStatement.arg !== capability) { @@ -326,10 +359,32 @@ export class YangParser { const isUnavailable = this._unavailableCapabilities.some(c => c.capability === capability); const isImportOnly = parentImportOnlyModule === true || this._importOnlyModules.some(c => c.name === capability); + + // extract revisions + const revisions = this.extractNodes(rootStatement, 'revision').reduce<{ [version: string]: {} }>((acc, revision) => { + if (!revision.arg) { + throw new Error(`Module [${rootStatement.arg}] has a version w/o version number.`); + } + const description = this.extractValue(revision, 'description'); + const reference = this.extractValue(revision, 'reference'); + acc[revision.arg] = { + description, + reference, + }; + return acc; + }, {}); + + const latestVersionLoaded = Object.keys(revisions).sort().reverse()[0]; + if (existingCapability && latestVersionExisting >= latestVersionLoaded) { + if (LOGLEVEL == LogLevel.Warning) { + console.warn(`Skipped capability: ${capability}:${latestVersionLoaded} since ${capability}:${latestVersionExisting} already contained.`); + } + return; + } const module = this._modules[capability] = { name: rootStatement.arg, - revisions: {}, + revisions, imports: {}, features: {}, identities: {}, @@ -339,10 +394,10 @@ export class YangParser { views: {}, elements: {}, state: isUnavailable - ? ModuleState.unavailable - : isImportOnly - ? ModuleState.importOnly - : ModuleState.stable, + ? ModuleState.unavailable + : isImportOnly + ? ModuleState.importOnly + : ModuleState.stable, }; await this.handleModule(module, rootStatement, capability); @@ -351,84 +406,66 @@ export class YangParser { private async handleModule(module: Module, rootStatement: Statement, capability: string) { // extract namespace && prefix - module.namespace = this.extractValue(rootStatement, "namespace"); - module.prefix = this.extractValue(rootStatement, "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"); + 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"); + const description = this.extractValue(feature, 'description'); acc[feature.arg] = { description, }; return acc; - }, {}) + }, {}), }; // extract imports - const imports = this.extractNodes(rootStatement, "import"); + 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"); + 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 and set module state if (imports) for (let ind = 0; ind < imports.length; ++ind) { - const moduleName = imports[ind].arg!; + const moduleName = imports[ind].arg!; - //TODO: Fix imports getting loaded without revision - await this.addCapability(moduleName, undefined, module.state === ModuleState.importOnly); + const revision = this._capabilityRevisionMap[moduleName] || undefined; + await this.addCapability(moduleName, revision, module.state === ModuleState.importOnly); const importedModule = this._modules[imports[ind].arg!]; if (importedModule && importedModule.state > ModuleState.stable) { - module.state = Math.max(module.state, ModuleState.instable); + module.state = Math.max(module.state, ModuleState.instable); } } - this.extractTypeDefinitions(rootStatement, module, ""); + this.extractTypeDefinitions(rootStatement, module, ''); - this.extractIdentities(rootStatement, 0, module, ""); + this.extractIdentities(rootStatement, 0, module, ''); - const groupings = this.extractGroupings(rootStatement, 0, module, ""); + const groupings = this.extractGroupings(rootStatement, 0, module, ''); this._views.push(...groupings); - const augments = this.extractAugments(rootStatement, 0, module, ""); + 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, ""); + const [currentView, subViews] = this.extractSubViews(rootStatement, 0, module, ''); this._views.push(currentView, ...subViews); // create the root elements for this module @@ -443,7 +480,7 @@ export class YangParser { const viewIdIndex = Number(viewElement.viewId); module.views[key] = this._views[viewIdIndex]; } - + // add only the UI View if the module is available if (module.state === ModuleState.stable || module.state === ModuleState.instable) this._views[0].elements[key] = module.elements[key]; }); @@ -462,7 +499,7 @@ export class YangParser { // process all groupings this._groupingsToResolve.filter(vs => vs.uses && vs.uses[ResolveFunction]).forEach(vs => { - try { vs.uses![ResolveFunction] !== undefined && vs.uses![ResolveFunction]!("|"); } catch (error) { + try { vs.uses![ResolveFunction] !== undefined && vs.uses![ResolveFunction]!('|'); } catch (error) { console.warn(`Error resolving: [${vs.name}] [${error.message}]`); } }); @@ -471,16 +508,16 @@ export class YangParser { * This is to fix the issue for sequential execution of modules based on their child and parent relationship * We are sorting the module object based on their augment status */ - Object.keys(this.modules) + Object.keys(this.modules) .map(elem => { - if(this.modules[elem].augments && Object.keys(this.modules[elem].augments).length > 0) { - const {augments, ...rest} = this.modules[elem]; - const partsOfKeys = Object.keys(augments).map((key) => (key.split("/").length - 1)) - this.modules[elem].executionOrder= Math.max(...partsOfKeys) - } else { - this.modules[elem].executionOrder=0; - } - }) + if (this.modules[elem].augments && Object.keys(this.modules[elem].augments).length > 0) { + const { augments, ..._rest } = this.modules[elem]; + const partsOfKeys = Object.keys(augments).map((key) => (key.split('/').length - 1)); + this.modules[elem].executionOrder = Math.max(...partsOfKeys); + } else { + this.modules[elem].executionOrder = 0; + } + }); // process all augmentations / sort by namespace changes to ensure proper order Object.keys(this.modules).sort((a, b) => this.modules[a].executionOrder! - this.modules[b].executionOrder!).forEach(modKey => { @@ -489,8 +526,8 @@ export class YangParser { const pathParts = splitVPath(key, /(?:(?:([^\/\:]+):)?([^\/]+))/g); // 1 = opt: namespace / 2 = property let nameSpaceChangeCounter = 0; let currentNS = module.name; // init namespace - pathParts.forEach(([ns, _])=> { - if (ns === currentNS){ + pathParts.forEach(([ns, _]) => { + if (ns === currentNS) { currentNS = ns; nameSpaceChangeCounter++; } @@ -498,11 +535,11 @@ export class YangParser { return { key, nameSpaceChangeCounter, - } + }; }); - + const augmentKeys = augmentKeysWithCounter - .sort((a,b) => a.nameSpaceChangeCounter > b.nameSpaceChangeCounter ? 1 : a.nameSpaceChangeCounter === b.nameSpaceChangeCounter ? 0 : -1 ) + .sort((a, b) => a.nameSpaceChangeCounter > b.nameSpaceChangeCounter ? 1 : a.nameSpaceChangeCounter === b.nameSpaceChangeCounter ? 0 : -1) .map((a) => a.key); augmentKeys.forEach(augKey => { @@ -512,11 +549,23 @@ export class YangParser { if (augments && viewSpec) { augments.forEach(augment => Object.keys(augment.elements).forEach(key => { const elm = augment.elements[key]; + + const when = elm.when && augment.when + ? { + type: WhenTokenType.AND, + left: elm.when, + right: augment.when, + } + : elm.when || augment.when; + + const ifFeature = elm.ifFeature + ? `(${augment.ifFeature}) and (${elm.ifFeature})` + : augment.ifFeature; + 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, + when, + ifFeature, }; })); } @@ -534,7 +583,7 @@ export class YangParser { } } return result; - } + }; const baseIdentities: Identity[] = []; Object.keys(this.modules).forEach(modKey => { @@ -565,30 +614,31 @@ export class YangParser { } }); - // resolve readOnly - const resolveReadOnly = (view: ViewSpecification, parentConfig: boolean) => { - - // update view config - view.config = view.config && parentConfig; - - Object.keys(view.elements).forEach((key) => { - const elm = view.elements[key]; - - // update element config - elm.config = elm.config && view.config; - - // update all sub-elements of objects - if (elm.uiType === "object") { - resolveReadOnly(this.views[+elm.viewId], elm.config); - } + // // resolve readOnly + // const resolveReadOnly = (view: ViewSpecification, parentConfig: boolean) => { - }) - } + // // update view config + // view.config = view.config && parentConfig; - const dump = resolveReadOnly(this.views[0], true); - }; + // Object.keys(view.elements).forEach((key) => { + // const elm = view.elements[key]; + + // // update element config + // elm.config = elm.config && view.config; + + // // update all sub-elements of objects + // if (elm.uiType === 'object') { + // resolveReadOnly(this.views[+elm.viewId], elm.config); + // } + + // }); + // }; + + // const dump = resolveReadOnly(this.views[0], true); + } private _nextId = 1; + private get nextId() { return this._nextId++; } @@ -608,7 +658,7 @@ export class YangParser { } private extractTypeDefinitions(statement: Statement, module: Module, currentPath: string): void { - const typedefs = this.extractNodes(statement, "typedef"); + const typedefs = this.extractNodes(statement, 'typedef'); typedefs && typedefs.forEach(def => { if (!def.arg) { throw new Error(`Module: [${module.name}]. Found typefed without name.`); @@ -620,7 +670,7 @@ export class YangParser { /** Handles groupings like named Container */ private extractGroupings(statement: Statement, parentId: number, module: Module, currentPath: string): ViewSpecification[] { const subViews: ViewSpecification[] = []; - const groupings = this.extractNodes(statement, "grouping"); + const groupings = this.extractNodes(statement, 'grouping'); if (groupings && groupings.length > 0) { subViews.push(...groupings.reduce((acc, cur) => { if (!cur.arg) { @@ -629,9 +679,9 @@ export class YangParser { const grouping = cur.arg; // the default for config on module level is config = true; - const [currentView, subViews] = this.extractSubViews(cur, /* parentId */ -1, module, currentPath); + const [currentView, currentSubViews] = this.extractSubViews(cur, /* parentId */ -1, module, currentPath); grouping && (module.groupings[grouping] = currentView); - acc.push(currentView, ...subViews); + acc.push(currentView, ...currentSubViews); return acc; }, [])); } @@ -642,7 +692,7 @@ export class YangParser { /** Handles augments also like named container */ private extractAugments(statement: Statement, parentId: number, module: Module, currentPath: string): ViewSpecification[] { const subViews: ViewSpecification[] = []; - const augments = this.extractNodes(statement, "augment"); + const augments = this.extractNodes(statement, 'augment'); if (augments && augments.length > 0) { subViews.push(...augments.reduce((acc, cur) => { if (!cur.arg) { @@ -651,12 +701,12 @@ export class YangParser { 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); + const [currentView, currentSubViews] = this.extractSubViews(cur, parentId, module, currentPath); if (augment) { module.augments[augment] = module.augments[augment] || []; module.augments[augment].push(currentView); } - acc.push(currentView, ...subViews); + acc.push(currentView, ...currentSubViews); return acc; }, [])); } @@ -666,109 +716,109 @@ export class YangParser { /** Handles identities */ private extractIdentities(statement: Statement, parentId: number, module: Module, currentPath: string) { - const identities = this.extractNodes(statement, "identity"); + 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.`); + throw new Error(`Module: [${module.name}][${currentPath}]. Found identity 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: [] - } + base: this.extractValue(cur, 'base'), + description: this.extractValue(cur, 'description'), + reference: this.extractValue(cur, 'reference'), + children: [], + }; return acc; }, {}); } - // Hint: use 0 as parentId for rootElements and -1 for rootGroupings. + // Hint: use 0 as parentId for rootElements and -1 for rootGroupings. private extractSubViews(statement: Statement, parentId: number, module: Module, currentPath: string): [ViewSpecification, ViewSpecification[]] { // used for scoped definitions const context: Module = { ...module, typedefs: { - ...module.typedefs - } + ...module.typedefs, + }, }; const currentId = this.nextId; const subViews: ViewSpecification[] = []; let elements: ViewElement[] = []; - const configValue = this.extractValue(statement, "config"); - const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false"; + 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"); - if (whenCondition) console.warn("Found in [" + context.name + "]" + currentPath + " when: " + whenCondition); + const ifFeature = this.extractValue(statement, 'if-feature'); + const whenCondition = this.extractValue(statement, 'when'); + if (whenCondition) console.warn('Found in [' + context.name + ']' + currentPath + ' when: ' + whenCondition); // extract all scoped typedefs this.extractTypeDefinitions(statement, context, currentPath); // extract all scoped groupings subViews.push( - ...this.extractGroupings(statement, parentId, context, currentPath) + ...this.extractGroupings(statement, parentId, context, currentPath), ); // extract all container - const container = this.extractNodes(statement, "container"); + const container = this.extractNodes(statement, 'container'); if (container && container.length > 0) { subViews.push(...container.reduce((acc, cur) => { if (!cur.arg) { throw new Error(`Module: [${context.name}]${currentPath}. Found container without name.`); } - const [currentView, subViews] = this.extractSubViews(cur, currentId, context, `${currentPath}/${context.name}:${cur.arg}`); + const [currentView, currentSubViews] = this.extractSubViews(cur, currentId, context, `${currentPath}/${context.name}:${cur.arg}`); elements.push({ id: parentId === 0 ? `${context.name}:${cur.arg}` : cur.arg, label: cur.arg, path: currentPath, module: context.name || module.name || '', - uiType: "object", + uiType: 'object', viewId: currentView.id, config: currentView.config, }); - acc.push(currentView, ...subViews); + acc.push(currentView, ...currentSubViews); 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"); + const lists = this.extractNodes(statement, 'list'); if (lists && lists.length > 0) { subViews.push(...lists.reduce((acc, cur) => { let elmConfig = config; if (!cur.arg) { throw new Error(`Module: [${context.name}]${currentPath}. Found list without name.`); } - const key = this.extractValue(cur, "key") || undefined; + const key = this.extractValue(cur, 'key') || undefined; if (elmConfig && !key) { console.warn(`Module: [${context.name}]${currentPath}. Found configurable list without key. Assume config shell be false.`); elmConfig = false; } - const [currentView, subViews] = this.extractSubViews(cur, currentId, context, `${currentPath}/${context.name}:${cur.arg}`); + const [currentView, currentSubViews] = this.extractSubViews(cur, currentId, context, `${currentPath}/${context.name}:${cur.arg}`); elements.push({ id: parentId === 0 ? `${context.name}:${cur.arg}` : cur.arg, label: cur.arg, path: currentPath, module: context.name || module.name || '', isList: true, - uiType: "object", + uiType: 'object', viewId: currentView.id, key: key, config: elmConfig && currentView.config, }); - acc.push(currentView, ...subViews); + acc.push(currentView, ...currentSubViews); return acc; }, [])); } // process all leaf-lists // a leaf-list is a list of some type - const leafLists = this.extractNodes(statement, "leaf-list"); + const leafLists = this.extractNodes(statement, 'leaf-list'); if (leafLists && leafLists.length > 0) { elements.push(...leafLists.reduce((acc, cur) => { const element = this.getViewElement(cur, context, parentId, currentPath, true); @@ -779,7 +829,7 @@ export class YangParser { // process all leafs // a leaf is mainly a property of an object - const leafs = this.extractNodes(statement, "leaf"); + const leafs = this.extractNodes(statement, 'leaf'); if (leafs && leafs.length > 0) { elements.push(...leafs.reduce((acc, cur) => { const element = this.getViewElement(cur, context, parentId, currentPath, false); @@ -789,92 +839,92 @@ export class YangParser { } - const choiceStms = this.extractNodes(statement, "choice"); + const choiceStms = this.extractNodes(statement, 'choice'); if (choiceStms && choiceStms.length > 0) { - elements.push(...choiceStms.reduce((accChoise, curChoise) => { - if (!curChoise.arg) { + elements.push(...choiceStms.reduce((accChoice, curChoice) => { + if (!curChoice.arg) { throw new Error(`Module: [${context.name}]${currentPath}. Found choise without name.`); } // extract all cases like containers - const cases: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } }[] = []; - const caseStms = this.extractNodes(curChoise, "case"); + const cases: { id: string; label: string; description?: string; elements: { [name: string]: ViewElement } }[] = []; + const caseStms = this.extractNodes(curChoice, 'case'); if (caseStms && caseStms.length > 0) { cases.push(...caseStms.reduce((accCase, curCase) => { if (!curCase.arg) { - throw new Error(`Module: [${context.name}]${currentPath}/${curChoise.arg}. Found case without name.`); + throw new Error(`Module: [${context.name}]${currentPath}/${curChoice.arg}. Found case without name.`); } - const description = this.extractValue(curCase, "description") || undefined; - const [caseView, caseSubViews] = this.extractSubViews(curCase, parentId, context, `${currentPath}/${context.name}:${curChoise.arg}`); + const description = this.extractValue(curCase, 'description') || undefined; + const [caseView, caseSubViews] = this.extractSubViews(curCase, parentId, context, `${currentPath}/${context.name}:${curChoice.arg}`); subViews.push(caseView, ...caseSubViews); - const caseDef: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } = { + const caseDef: { id: string; label: string; description?: string; elements: { [name: string]: ViewElement } } = { id: parentId === 0 ? `${context.name}:${curCase.arg}` : curCase.arg, label: curCase.arg, description: description, - elements: caseView.elements + elements: caseView.elements, }; accCase.push(caseDef); return accCase; - }, [] as { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } }[])); + }, [] as { id: string; label: string; description?: string; elements: { [name: string]: ViewElement } }[])); } // extract all simple cases (one case per leaf, container, etc.) - const [choiseView, choiseSubViews] = this.extractSubViews(curChoise, parentId, context, `${currentPath}/${context.name}:${curChoise.arg}`); - subViews.push(choiseView, ...choiseSubViews); - cases.push(...Object.keys(choiseView.elements).reduce((accElm, curElm) => { - const elm = choiseView.elements[curElm]; - const caseDef: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } = { + const [choiceView, choiceSubViews] = this.extractSubViews(curChoice, parentId, context, `${currentPath}/${context.name}:${curChoice.arg}`); + subViews.push(choiceView, ...choiceSubViews); + cases.push(...Object.keys(choiceView.elements).reduce((accElm, curElm) => { + const elm = choiceView.elements[curElm]; + const caseDef: { id: string; label: string; description?: string; elements: { [name: string]: ViewElement } } = { id: elm.id, label: elm.label, description: elm.description, - elements: { [elm.id]: elm } + elements: { [elm.id]: elm }, }; accElm.push(caseDef); return accElm; - }, [] as { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } }[])); + }, [] as { id: string; label: string; description?: string; elements: { [name: string]: ViewElement } }[])); - const description = this.extractValue(curChoise, "description") || undefined; - const configValue = this.extractValue(curChoise, "config"); - const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false"; + const choiceDescription = this.extractValue(curChoice, 'description') || undefined; + const choiceConfigValue = this.extractValue(curChoice, 'config'); + const choiceConfig = choiceConfigValue == null ? true : choiceConfigValue.toLocaleLowerCase() !== 'false'; - const mandatory = this.extractValue(curChoise, "mandatory") === "true" || false; + const mandatory = this.extractValue(curChoice, 'mandatory') === 'true' || false; - const element: ViewElementChoise = { - uiType: "choise", - id: parentId === 0 ? `${context.name}:${curChoise.arg}` : curChoise.arg, - label: curChoise.arg, + const element: ViewElementChoice = { + uiType: 'choice', + id: parentId === 0 ? `${context.name}:${curChoice.arg}` : curChoice.arg, + label: curChoice.arg, path: currentPath, module: context.name || module.name || '', - config: config, + config: choiceConfig, mandatory: mandatory, - description: description, + description: choiceDescription, cases: cases.reduce((acc, cur) => { acc[cur.id] = cur; return acc; - }, {} as { [name: string]: { id: string, label: string, description?: string, elements: { [name: string]: ViewElement } } }) + }, {} as { [name: string]: { id: string; label: string; description?: string; elements: { [name: string]: ViewElement } } }), }; - accChoise.push(element); - return accChoise; + accChoice.push(element); + return accChoice; }, [])); } - const rpcStms = this.extractNodes(statement, "rpc"); + const rpcStms = this.extractNodes(statement, 'rpc'); if (rpcStms && rpcStms.length > 0) { elements.push(...rpcStms.reduce((accRpc, curRpc) => { if (!curRpc.arg) { throw new Error(`Module: [${context.name}]${currentPath}. Found rpc without name.`); } - const description = this.extractValue(curRpc, "description") || undefined; - const configValue = this.extractValue(curRpc, "config"); - const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false"; + const rpcDescription = this.extractValue(curRpc, 'description') || undefined; + const rpcConfigValue = this.extractValue(curRpc, 'config'); + const rpcConfig = rpcConfigValue == null ? true : rpcConfigValue.toLocaleLowerCase() !== 'false'; let inputViewId: string | undefined = undefined; let outputViewId: string | undefined = undefined; - const input = this.extractNodes(curRpc, "input") || undefined; - const output = this.extractNodes(curRpc, "output") || undefined; + const input = this.extractNodes(curRpc, 'input') || undefined; + const output = this.extractNodes(curRpc, 'output') || undefined; if (input && input.length > 0) { const [inputView, inputSubViews] = this.extractSubViews(input[0], parentId, context, `${currentPath}/${context.name}:${curRpc.arg}`); @@ -889,13 +939,13 @@ export class YangParser { } const element: ViewElementRpc = { - uiType: "rpc", + uiType: 'rpc', id: parentId === 0 ? `${context.name}:${curRpc.arg}` : curRpc.arg, label: curRpc.arg, path: currentPath, module: context.name || module.name || '', - config: config, - description: description, + config: rpcConfig, + description: rpcDescription, inputViewId: inputViewId, outputViewId: outputViewId, }; @@ -906,9 +956,16 @@ export class YangParser { }, [])); } - // if (!statement.arg) { - // throw new Error(`Module: [${context.name}]. Found statement without name.`); - // } + if (!statement.arg) { + console.error(new Error(`Module: [${context.name}]. Found statement without name.`)); + } + + let whenParsed: WhenAST | undefined = undefined; + try { + whenParsed = whenCondition && parseWhen(whenCondition) || undefined; + } catch (e) { + console.error(new Error(`Module: [${context.name}]. Found invalid when condition: ${whenCondition}`)); + } const viewSpec: ViewSpecification = { id: String(currentId), @@ -916,11 +973,11 @@ export class YangParser { ns: context.name, name: statement.arg != null ? statement.arg : undefined, title: statement.arg != null ? statement.arg : undefined, - language: "en-us", + language: 'en-us', canEdit: false, config: config, ifFeature: ifFeature, - when: whenCondition, + when: whenParsed, elements: elements.reduce<{ [name: string]: ViewElement }>((acc, cur) => { acc[cur.id] = cur; return acc; @@ -928,21 +985,21 @@ export class YangParser { }; // evaluate canEdit depending on all conditions - Object.defineProperty(viewSpec, "canEdit", { + 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"); + const usesRefs = this.extractNodes(statement, 'uses'); if (usesRefs && usesRefs.length > 0) { viewSpec.uses = (viewSpec.uses || []); - const resolveFunctions : ((parentElementPath: string)=>void)[] = []; + const resolveFunctions: ((parentElementPath: string) => void)[] = []; for (let i = 0; i < usesRefs.length; ++i) { const groupingName = usesRefs[i].arg; @@ -951,7 +1008,7 @@ export class YangParser { } viewSpec.uses.push(this.resolveReferencePath(groupingName, context)); - + resolveFunctions.push((parentElementPath: string) => { const groupingViewSpec = this.resolveGrouping(groupingName, context); if (groupingViewSpec) { @@ -963,10 +1020,22 @@ export class YangParser { Object.keys(groupingViewSpec.elements).forEach(key => { const elm = groupingViewSpec.elements[key]; // a useRef on root level need a namespace + const resolvedWhen = elm.when && groupingViewSpec.when + ? { + type: WhenTokenType.AND, + left: elm.when, + right: groupingViewSpec.when, + } + : elm.when || groupingViewSpec.when; + + const resolvedIfFeature = elm.ifFeature + ? `(${groupingViewSpec.ifFeature}) and (${elm.ifFeature})` + : groupingViewSpec.ifFeature; + viewSpec.elements[parentId === 0 ? `${module.name}:${key}` : key] = { ...elm, - when: elm.when ? `(${groupingViewSpec.when}) and (${elm.when})` : groupingViewSpec.when, - ifFeature: elm.ifFeature ? `(${groupingViewSpec.ifFeature}) and (${elm.ifFeature})` : groupingViewSpec.ifFeature, + when: resolvedWhen, + ifFeature: resolvedIfFeature, }; }); } @@ -974,19 +1043,19 @@ export class YangParser { } viewSpec.uses[ResolveFunction] = (parentElementPath: string) => { - const currentElementPath = `${parentElementPath} -> ${viewSpec.ns}:${viewSpec.name}`; + const currentElementPath = `${parentElementPath} -> ${viewSpec.ns}:${viewSpec.name}`; resolveFunctions.forEach(resolve => { - try { - resolve(currentElementPath); - } catch (error) { - console.error(error); - } + try { + resolve(currentElementPath); + } catch (error) { + console.error(error); + } }); // console.log("Resolved "+currentElementPath, viewSpec); if (viewSpec?.uses) { viewSpec.uses[ResolveFunction] = undefined; } - } + }; this._groupingsToResolve.push(viewSpec); } @@ -1020,28 +1089,28 @@ export class YangParser { /** 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 type = this.extractValue(cur, 'type'); + const defaultVal = this.extractValue(cur, 'default') || undefined; + const description = this.extractValue(cur, 'description') || undefined; - const configValue = this.extractValue(cur, "config"); - const config = configValue == null ? true : configValue.toLocaleLowerCase() !== "false"; + const configValue = this.extractValue(cur, 'config'); + const config = configValue == null ? true : configValue.toLocaleLowerCase() !== 'false'; - const extractRange = (min: number, max: number, property: string = "range"): { expression: Expression | undefined, min: number, max: number } => { - const ranges = this.extractValue(this.extractNodes(cur, "type")[0]!, property) || undefined; - const range = ranges ?.replace(/min/i, String(min)).replace(/max/i, String(max)).split("|").map(r => { + const extractRange = (min: number, max: number, property: string = 'range'): { expression: Expression | undefined; min: number; max: number } => { + const ranges = this.extractValue(this.extractNodes(cur, 'type')[0]!, property) || undefined; + const range = ranges?.replace(/min/i, String(min)).replace(/max/i, String(max)).split('|').map(r => { let minValue: number; let maxValue: number; - + if (r.indexOf('..') > -1) { - const [minStr, maxStr] = r.split('..'); - minValue = Number(minStr); - maxValue = Number(maxStr); - } else if (!isNaN(maxValue = Number(r && r.trim() )) ) { - minValue = maxValue; + const [minStr, maxStr] = r.split('..'); + minValue = Number(minStr); + maxValue = Number(maxStr); + } else if (!isNaN(maxValue = Number(r && r.trim()))) { + minValue = maxValue; } else { - minValue = min, - maxValue = max; + minValue = min, + maxValue = max; } if (minValue > min) min = minValue; @@ -1049,7 +1118,7 @@ export class YangParser { return { min: minValue, - max: maxValue + max: maxValue, }; }); return { @@ -1058,21 +1127,22 @@ export class YangParser { expression: range && range.length === 1 ? range[0] : range && range.length > 1 - ? { operation: "OR", arguments: range } - : undefined - } + ? { operation: 'OR', arguments: range } + : undefined, + }; }; const extractPattern = (): Expression | undefined => { - const pattern = this.extractNodes(this.extractNodes(cur, "type")[0]!, "pattern").map(p => p.arg!).filter(p => !!p).map(p => `^${p.replace(/(?:\\(.))/g, '$1')}$`); + // 2023.01.26 decision MF & SKO: we will no longer remove the backslashes from the pattern, seems to be a bug in the original code + const pattern = this.extractNodes(this.extractNodes(cur, 'type')[0]!, 'pattern').map(p => p.arg!).filter(p => !!p).map(p => `^${p/*.replace(/(?:\\(.))/g, '$1')*/}$`); return pattern && pattern.length == 1 ? new RegExp(pattern[0]) : pattern && pattern.length > 1 - ? { operation: "AND", arguments: pattern.map(p => new RegExp(p)) } + ? { operation: 'AND', arguments: pattern.map(p => new RegExp(p)) } : undefined; - } + }; - const mandatory = this.extractValue(cur, "mandatory") === "true" || false; + const mandatory = this.extractValue(cur, 'mandatory') === 'true' || false; if (!cur.arg) { throw new Error(`Module: [${module.name}]. Found element without name.`); @@ -1084,159 +1154,159 @@ export class YangParser { const element: ViewElementBase = { id: parentId === 0 ? `${module.name}:${cur.arg}` : cur.arg, - label: cur.arg, + label: cur.arg, path: currentPath, - module: module.name || "", + module: module.name || '', config: config, mandatory: mandatory, isList: isList, default: defaultVal, - description: description + description: description, }; - if (type === "string") { - const length = extractRange(0, +18446744073709551615, "length"); + if (type === 'string') { + const length = extractRange(0, +18446744073709551615, 'length'); return ({ ...element, - uiType: "string", + uiType: 'string', length: length.expression, pattern: extractPattern(), }); - } else if (type === "boolean") { + } else if (type === 'boolean') { return ({ ...element, - uiType: "boolean" + uiType: 'boolean', }); - } else if (type === "uint8") { + } else if (type === 'uint8') { const range = extractRange(0, +255); return ({ ...element, - uiType: "number", + uiType: 'number', range: range.expression, min: range.min, max: range.max, - units: this.extractValue(cur, "units") || undefined, - format: this.extractValue(cur, "format") || undefined, + units: this.extractValue(cur, 'units') || undefined, + format: this.extractValue(cur, 'format') || undefined, }); - } else if (type === "uint16") { + } else if (type === 'uint16') { const range = extractRange(0, +65535); return ({ ...element, - uiType: "number", + uiType: 'number', range: range.expression, min: range.min, max: range.max, - units: this.extractValue(cur, "units") || undefined, - format: this.extractValue(cur, "format") || undefined, + units: this.extractValue(cur, 'units') || undefined, + format: this.extractValue(cur, 'format') || undefined, }); - } else if (type === "uint32") { + } else if (type === 'uint32') { const range = extractRange(0, +4294967295); return ({ ...element, - uiType: "number", + uiType: 'number', range: range.expression, min: range.min, max: range.max, - units: this.extractValue(cur, "units") || undefined, - format: this.extractValue(cur, "format") || undefined, + units: this.extractValue(cur, 'units') || undefined, + format: this.extractValue(cur, 'format') || undefined, }); - } else if (type === "uint64") { + } else if (type === 'uint64') { const range = extractRange(0, +18446744073709551615); return ({ ...element, - uiType: "number", + uiType: 'number', range: range.expression, min: range.min, max: range.max, - units: this.extractValue(cur, "units") || undefined, - format: this.extractValue(cur, "format") || undefined, + units: this.extractValue(cur, 'units') || undefined, + format: this.extractValue(cur, 'format') || undefined, }); - } else if (type === "int8") { + } else if (type === 'int8') { const range = extractRange(-128, +127); return ({ ...element, - uiType: "number", + uiType: 'number', range: range.expression, min: range.min, max: range.max, - units: this.extractValue(cur, "units") || undefined, - format: this.extractValue(cur, "format") || undefined, + units: this.extractValue(cur, 'units') || undefined, + format: this.extractValue(cur, 'format') || undefined, }); - } else if (type === "int16") { + } else if (type === 'int16') { const range = extractRange(-32768, +32767); return ({ ...element, - uiType: "number", + uiType: 'number', range: range.expression, min: range.min, max: range.max, - units: this.extractValue(cur, "units") || undefined, - format: this.extractValue(cur, "format") || undefined, + units: this.extractValue(cur, 'units') || undefined, + format: this.extractValue(cur, 'format') || undefined, }); - } else if (type === "int32") { + } else if (type === 'int32') { const range = extractRange(-2147483648, +2147483647); return ({ ...element, - uiType: "number", + uiType: 'number', range: range.expression, min: range.min, max: range.max, - units: this.extractValue(cur, "units") || undefined, - format: this.extractValue(cur, "format") || undefined, + units: this.extractValue(cur, 'units') || undefined, + format: this.extractValue(cur, 'format') || undefined, }); - } else if (type === "int64") { + } else if (type === 'int64') { const range = extractRange(-9223372036854775808, +9223372036854775807); return ({ ...element, - uiType: "number", + uiType: 'number', range: range.expression, min: range.min, max: range.max, - units: this.extractValue(cur, "units") || undefined, - format: this.extractValue(cur, "format") || undefined, + units: this.extractValue(cur, 'units') || undefined, + format: this.extractValue(cur, 'format') || undefined, }); - } else if (type === "decimal64") { + } else if (type === 'decimal64') { // decimalRange - const fDigits = Number(this.extractValue(this.extractNodes(cur, "type")[0]!, "fraction-digits")) || -1; + const fDigits = Number(this.extractValue(this.extractNodes(cur, 'type')[0]!, 'fraction-digits')) || -1; if (fDigits === -1) { throw new Error(`Module: [${module.name}][${currentPath}][${cur.arg}]. Found decimal64 with invalid fraction-digits.`); } const range = extractRange(YangParser.decimalRange[fDigits].min, YangParser.decimalRange[fDigits].max); return ({ ...element, - uiType: "number", + uiType: 'number', fDigits: fDigits, range: range.expression, min: range.min, max: range.max, - units: this.extractValue(cur, "units") || undefined, - format: this.extractValue(cur, "format") || undefined, + units: this.extractValue(cur, 'units') || undefined, + format: this.extractValue(cur, 'format') || undefined, }); - } else if (type === "enumeration") { - const typeNode = this.extractNodes(cur, "type")[0]!; - const enumNodes = this.extractNodes(typeNode, "enum"); + } else if (type === 'enumeration') { + const typeNode = this.extractNodes(cur, 'type')[0]!; + const enumNodes = this.extractNodes(typeNode, 'enum'); return ({ ...element, - uiType: "selection", + 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 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 + 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"); + } 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.`); } @@ -1244,11 +1314,11 @@ export class YangParser { const resolve = this.resolveReference.bind(this); const res: ViewElement = { ...element, - uiType: "reference", + uiType: 'reference', referencePath: refPath, - ref(this: ViewElement, currentPath: string) { - const elementPath = `${currentPath}/${cur.arg}`; - + ref(this: ViewElement, basePath: string) { + const elementPath = `${basePath}/${cur.arg}`; + const result = resolve(refPath, elementPath); if (!result) return undefined; @@ -1262,20 +1332,20 @@ export class YangParser { isList: this.isList, default: this.default, description: this.description, - } as ViewElement , resolvedPath] || undefined; - } + } as ViewElement, resolvedPath] || undefined; + }, }; return res; - } else if (type === "identityref") { - const typeNode = this.extractNodes(cur, "type")[0]!; - const base = this.extractValue(typeNode, "base"); + } 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: [] + uiType: 'selection', + options: [], }; this._identityToResolve.push(() => { const identity: Identity = this.resolveIdentity(base, module); @@ -1288,29 +1358,29 @@ export class YangParser { res.options = identity.values.map(val => ({ key: val.id, value: val.id, - description: val.description + description: val.description, })); }); return res; - } else if (type === "empty") { + } 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. */ return { ...element, - uiType: "empty", + uiType: 'empty', }; - } else if (type === "union") { + } else if (type === 'union') { // todo: ❗ handle union ⚡ /* 9.12. The union Built-In Type */ - const typeNode = this.extractNodes(cur, "type")[0]!; - const typeNodes = this.extractNodes(typeNode, "type"); + const typeNode = this.extractNodes(cur, 'type')[0]!; + const typeNodes = this.extractNodes(typeNode, 'type'); const resultingElement = { ...element, - uiType: "union", - elements: [] + uiType: 'union', + elements: [], } as ViewElementUnion; const resolveUnion = () => { @@ -1318,13 +1388,13 @@ export class YangParser { const stm: Statement = { ...cur, sub: [ - ...(cur.sub ?.filter(s => s.key !== "type") || []), - node - ] + ...(cur.sub?.filter(s => s.key !== 'type') || []), + node, + ], }; return { ...this.getViewElement(stm, module, parentId, currentPath, isList), - id: node.arg! + id: node.arg!, }; })); }; @@ -1332,34 +1402,34 @@ export class YangParser { this._unionsToResolve.push(resolveUnion); return resultingElement; - } else if (type === "bits") { - const typeNode = this.extractNodes(cur, "type")[0]!; - const bitNodes = this.extractNodes(typeNode, "bit"); + } 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) => { + 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")); + // 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") { + } else if (type === 'binary') { return { ...element, - uiType: "binary", - length: extractRange(0, +18446744073709551615, "length"), + uiType: 'binary', + length: extractRange(0, +18446744073709551615, 'length'), }; - } else if (type === "instance-identifier") { + } else if (type === 'instance-identifier') { // https://tools.ietf.org/html/rfc7950#page-168 return { ...element, - uiType: "string", - length: extractRange(0, +18446744073709551615, "length"), + uiType: 'string', + length: extractRange(0, +18446744073709551615, 'length'), }; } else { // not a build in type, need to resolve type @@ -1374,13 +1444,13 @@ export class YangParser { } // spoof date type here from special string type - if ((type === 'date-and-time' || type.endsWith(':date-and-time') ) && typeRef.module === "ietf-yang-types") { - return { - ...typeRef, - ...element, - description: description, - uiType: "date", - }; + if ((type === 'date-and-time' || type.endsWith(':date-and-time')) && typeRef.module === 'ietf-yang-types') { + return { + ...typeRef, + ...element, + description: description, + uiType: 'date', + }; } return ({ @@ -1391,27 +1461,27 @@ export class YangParser { } } - private resolveStringType(parentElement: ViewElementString, pattern: Expression | undefined, length: { expression: Expression | undefined, min: number, max: number }) { + private resolveStringType(parentElement: ViewElementString, pattern: Expression | undefined, length: { expression: Expression | undefined; min: number; max: number }) { return { ...parentElement, pattern: pattern != null && parentElement.pattern - ? { operation: "AND", arguments: [pattern, parentElement.pattern] } + ? { operation: 'AND', arguments: [pattern, parentElement.pattern] } : parentElement.pattern ? parentElement.pattern : pattern, length: length.expression != null && parentElement.length - ? { operation: "AND", arguments: [length.expression, parentElement.length] } + ? { operation: 'AND', arguments: [length.expression, parentElement.length] } : parentElement.length ? parentElement.length - : length ?.expression, + : length?.expression, } as ViewElementString; } - private resolveNumberType(parentElement: ViewElementNumber, range: { expression: Expression | undefined, min: number, max: number }) { + private resolveNumberType(parentElement: ViewElementNumber, range: { expression: Expression | undefined; min: number; max: number }) { return { ...parentElement, range: range.expression != null && parentElement.range - ? { operation: "AND", arguments: [range.expression, parentElement.range] } + ? { operation: 'AND', arguments: [range.expression, parentElement.range] } : parentElement.range ? parentElement.range : range, @@ -1421,7 +1491,7 @@ export class YangParser { } private resolveReferencePath(vPath: string, module: Module) { - const vPathParser = /(?:(?:([^\/\:]+):)?([^\/]+))/g // 1 = opt: namespace / 2 = property + 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}`; @@ -1429,20 +1499,20 @@ export class YangParser { } private resolveReference(vPath: string, currentPath: string) { - const vPathParser = /(?:(?:([^\/\[\]\:]+):)?([^\/\[\]]+)(\[[^\]]+\])?)/g // 1 = opt: namespace / 2 = property / 3 = opt: indexPath + const vPathParser = /(?:(?:([^\/\[\]\:]+):)?([^\/\[\]]+)(\[[^\]]+\])?)/g; // 1 = opt: namespace / 2 = property / 3 = opt: indexPath let element: ViewElement | null = null; - let moduleName = ""; + 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 => { moduleName = p[1] || moduleName ; return { ns: moduleName, property: p[2], ind: p[3] } }) + const resultPathParts = !vPath.startsWith('/') + ? splitVPath(currentPath, vPathParser).map(p => { moduleName = p[1] || moduleName; return { ns: moduleName, property: p[2], ind: p[3] }; }) : []; for (let i = 0; i < vPathParts.length; ++i) { const vPathPart = vPathParts[i]; - if (vPathPart.property === "..") { + if (vPathPart.property === '..') { resultPathParts.pop(); - } else if (vPathPart.property !== ".") { + } else if (vPathPart.property !== '.') { resultPathParts.push(vPathPart); } } @@ -1453,30 +1523,30 @@ export class YangParser { if (j === 0) { moduleName = pathPart.ns; const rootModule = this._modules[moduleName]; - if (!rootModule) throw new Error("Could not resolve module [" + moduleName + "].\r\n" + vPath); + 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[pathPart.property] || view.elements[`${moduleName}:${pathPart.property}`]; } else { - throw new Error("Could not resolve reference.\r\n" + vPath); + 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); + if (!element) throw new Error('Could not resolve path [' + pathPart.property + '] in [' + currentPath + '] \r\n' + vPath); } - moduleName = ""; // create the vPath for the resolved element, do not add the element itself this will be done later in the res(...) function - return [element, resultPathParts.slice(0,-1).map(p => `${moduleName !== p.ns ? `${moduleName=p.ns}:` : ""}${p.property}${p.ind || ''}`).join("/")]; + moduleName = ''; // create the vPath for the resolved element, do not add the element itself this will be done later in the res(...) function + return [element, resultPathParts.slice(0, -1).map(p => `${moduleName !== p.ns ? `${moduleName = p.ns}:` : ''}${p.property}${p.ind || ''}`).join('/')]; } private resolveView(vPath: string) { - const vPathParser = /(?:(?:([^\/\[\]\:]+):)?([^\/\[\]]+)(\[[^\]]+\])?)/g // 1 = opt: namespace / 2 = property / 3 = opt: indexPath + 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 = ""; + let moduleName = ''; if (vPath) do { partMatch = vPathParser.exec(vPath); if (partMatch) { @@ -1498,13 +1568,13 @@ export class YangParser { } if (!element) return null; } - } while (partMatch) + } 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 collonInd = type.indexOf(':'); + const preFix = collonInd > -1 ? type.slice(0, collonInd) : ''; const typeName = collonInd > -1 ? type.slice(collonInd + 1) : type; const res = preFix @@ -1514,8 +1584,8 @@ export class YangParser { } private resolveGrouping(grouping: string, module: Module) { - const collonInd = grouping.indexOf(":"); - const preFix = collonInd > -1 ? grouping.slice(0, collonInd) : ""; + const collonInd = grouping.indexOf(':'); + const preFix = collonInd > -1 ? grouping.slice(0, collonInd) : ''; const groupingName = collonInd > -1 ? grouping.slice(collonInd + 1) : grouping; return preFix @@ -1525,8 +1595,8 @@ export class YangParser { } private resolveIdentity(identity: string, module: Module) { - const collonInd = identity.indexOf(":"); - const preFix = collonInd > -1 ? identity.slice(0, collonInd) : ""; + const collonInd = identity.indexOf(':'); + const preFix = collonInd > -1 ? identity.slice(0, collonInd) : ''; const identityName = collonInd > -1 ? identity.slice(collonInd + 1) : identity; return preFix diff --git a/sdnr/wt/odlux/apps/configurationApp/webpack.config.js b/sdnr/wt/odlux/apps/configurationApp/webpack.config.js index 57caf079f..0d37c7d87 100644 --- a/sdnr/wt/odlux/apps/configurationApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/configurationApp/webpack.config.js @@ -10,6 +10,7 @@ const path = require("path"); const webpack = require("webpack"); const CopyWebpackPlugin = require("copy-webpack-plugin"); const TerserPlugin = require('terser-webpack-plugin'); +const proxyConf = require('../../proxy.conf'); const policies = require('./policies.json'); @@ -59,6 +60,16 @@ module.exports = (env) => { use: [{ loader: "babel-loader" }] + },{ + //don't minify images + test: /\.(png|gif|jpg|svg)$/, + use: [{ + loader: 'url-loader', + options: { + limit: 10, + name: './images/[name].[ext]' + } + }] }] }, @@ -133,56 +144,7 @@ module.exports = (env) => { before: function(app, server, compiler) { app.get('/oauth/policies',(_, res) => res.json(policies)); }, - proxy: { - "/about": { - target: "http://sdnr:8181", - secure: false - }, - "/yang-schema/": { - target: "http://sdnr:8181", - secure: false - }, - "/oauth/": { - target: "http://sdnr:8181", - secure: false - }, - "/database/": { - target: "http://sdnr:8181", - secure: false - }, - "/restconf/": { - target: "http://sdnr:8181", - secure: false - }, - "/rests/": { - target: "http://sdnr:8181", - secure: false - }, - "/help/": { - target: "http://sdnr:8181", - secure: false - }, - "/about/": { - target: "http://sdnr:8181", - secure: false - }, - "/tree/": { - target: "http://sdnr:8181", - secure: false - }, - "/websocket": { - target: "http://sdnr:8181", - ws: true, - changeOrigin: true, - secure: false - }, - "/apidoc": { - target: "http://sdnr:8181", - ws: true, - changeOrigin: true, - secure: false - } - } + proxy: proxyConf, } }]; } diff --git a/sdnr/wt/odlux/apps/connectApp/package.json b/sdnr/wt/odlux/apps/connectApp/package.json index a31824ae2..9a3c35baa 100644 --- a/sdnr/wt/odlux/apps/connectApp/package.json +++ b/sdnr/wt/odlux/apps/connectApp/package.json @@ -29,6 +29,7 @@ "@odlux/framework": "*" }, "peerDependencies": { + "@fortawesome/free-solid-svg-icons": "5.6.3", "@types/classnames": "2.2.6", "@types/flux": "3.1.8", "@types/jquery": "3.3.10", diff --git a/sdnr/wt/odlux/apps/connectApp/pom.xml b/sdnr/wt/odlux/apps/connectApp/pom.xml index c12048ebb..c9509c7a4 100644 --- a/sdnr/wt/odlux/apps/connectApp/pom.xml +++ b/sdnr/wt/odlux/apps/connectApp/pom.xml @@ -19,6 +19,7 @@ ~ ============LICENSE_END======================================================= ~ --> + 4.0.0 @@ -139,7 +140,7 @@ initialize - v12.13.0 + v12.22.0 v1.22.10 diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts index 26aa8d2d7..948f2aada 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/commonNetworkElementsActions.ts @@ -1,5 +1,3 @@ -// 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======================================================================== @@ -19,16 +17,22 @@ * ============LICENSE_END========================================================================== */ +/** + * Create an update action that can distinguish whether one or the other view is currently active and update it. + * This action is then used for each update in the other actions and when notifications arrive. + * create an update action that can distinguish whether one or the other view is currently active and update it. + * This action is then used for each update in the other actions and when notifications arrive. + */ + 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 { networkElementsReloadAction } from '../handlers/networkElementsHandler'; import { guiCutThrough } from '../models/guiCutTrough'; -import { connectService} from '../services/connectService'; +import { PanelId } from '../models/panelId'; +import { connectService } from '../services/connectService'; export class SetPanelAction extends Action { @@ -51,7 +55,7 @@ export class RemoveWebUri extends Action { export const removeWebUriAction = (nodeId: string) => { return new RemoveWebUri(nodeId); -} +}; export class SetWeburiSearchBusy extends Action { constructor(public isbusy: boolean) { @@ -68,7 +72,7 @@ export const findWebUrisForGuiCutThroughAsyncAction = (networkElementIds: string return; isBusy = true; - const { connect: { guiCutThrough, networkElements } } = getState(); + const { connect: { guiCutThrough: guiCutThrough2, networkElements } } = getState(); let notConnectedElements: string[] = []; let elementsToSearch: string[] = []; @@ -78,16 +82,16 @@ export const findWebUrisForGuiCutThroughAsyncAction = (networkElementIds: string networkElementIds.forEach(id => { const item = networkElements.rows.find((ne) => ne.id === id); if (item) { - if (item.status === "Connected") { + if (item.status === 'Connected') { // if (item.coreModelCapability !== "Unsupported") { // element is connected and is added to search list, if it doesn't exist already - const exists = guiCutThrough.searchedElements.filter(element => element.id === id).length > 0; + const exists = guiCutThrough2.searchedElements.filter(element => element.id === id).length > 0; if (!exists) { elementsToSearch.push(id); //element was found previously, but wasn't connected - if (guiCutThrough.notSearchedElements.length > 0 && guiCutThrough.notSearchedElements.includes(id)) { + if (guiCutThrough2.notSearchedElements.length > 0 && guiCutThrough2.notSearchedElements.includes(id)) { prevFoundElements.push(id); } } @@ -104,10 +108,9 @@ export const findWebUrisForGuiCutThroughAsyncAction = (networkElementIds: string // } // } // } - } - else { + } else { // element isn't connected and cannot be searched for a weburi - if (!guiCutThrough.notSearchedElements.includes(id)) { + if (!guiCutThrough2.notSearchedElements.includes(id)) { notConnectedElements.push(item.id as string); } } @@ -121,18 +124,17 @@ export const findWebUrisForGuiCutThroughAsyncAction = (networkElementIds: string } 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") { + if (currentOpenPanel === 'NetworkElements') { return dispatch(networkElementsReloadAction); - } - else { + } 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 index bb744e236..120f9916f 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/actions/infoNetworkElementActions.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/infoNetworkElementActions.ts @@ -15,68 +15,68 @@ * the License. * ============LICENSE_END========================================================================== */ - import { Action } from '../../../../framework/src/flux/action'; - import { Dispatch } from '../../../../framework/src/flux/store'; +import { Action } from '../../../../framework/src/flux/action'; +import { Dispatch } from '../../../../framework/src/flux/store'; - import { Module, TopologyNode } from '../models/topologyNetconf'; - import { connectService } from '../services/connectService'; +import { Module, TopologyNode } from '../models/topologyNetconf'; +import { connectService } from '../services/connectService'; - /** +/** * Represents the base action. */ - export class BaseAction extends Action { } +export class BaseAction extends Action { } - /** +/** * Represents an action causing the store to load all element Yang capabilities. */ - export class LoadAllElementInfoAction extends BaseAction { } +export class LoadAllElementInfoAction extends BaseAction { } - /** +/** * Represents an action causing the store to update element Yang capabilities. */ - export class AllElementInfoLoadedAction extends BaseAction { - /** +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(); - } - } + constructor(public elementInfo: TopologyNode | null, public error?: string) { + super(); + } +} - /** +/** * Represents an action causing the store to update element Yang capabilities Module Features. */ - export class AllElementInfoFeatureLoadedAction extends BaseAction { - /** +export class AllElementInfoFeatureLoadedAction extends BaseAction { + /** * Initialize this instance. * @param elementFeatureInfo The information of the element which is returned. */ - constructor(public elementFeatureInfo: Module[] | null | undefined, public error?: string) { - super(); - } - } + constructor(public elementFeatureInfo: Module[] | null | undefined, 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)); - }); - } +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)); + }); +}; - /** +/** * Represents an asynchronous thunk action to load all yang features. */ - export const loadAllInfoElementFeaturesAsync = (nodeId: string) => (dispatch: Dispatch) => { - dispatch(new LoadAllElementInfoAction()); - connectService.infoNetworkElementFeatures(nodeId).then(infoFeatures => { - dispatch(new AllElementInfoFeatureLoadedAction(infoFeatures)); - }, error => { - dispatch(new AllElementInfoFeatureLoadedAction(null, error)); - }); - } \ No newline at end of file +export const loadAllInfoElementFeaturesAsync = (nodeId: string) => (dispatch: Dispatch) => { + dispatch(new LoadAllElementInfoAction()); + connectService.infoNetworkElementFeatures(nodeId).then(infoFeatures => { + dispatch(new AllElementInfoFeatureLoadedAction(infoFeatures)); + }, error => { + dispatch(new AllElementInfoFeatureLoadedAction(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 index 26ee7674f..11bac10e4 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/actions/mountedNetworkElementsActions.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/mountedNetworkElementsActions.ts @@ -32,7 +32,7 @@ export const mountNetworkElementAsyncActionCreator = (networkElement: NetworkEle return connectService.mountNetworkElement(networkElement).then((success) => { if (success) { dispatch(updateCurrentViewAsyncAction()); - dispatch(new AddSnackbarNotification({ message: `Requesting mount [${networkElement.nodeId}]`, options: { variant: 'info' } })) + dispatch(new AddSnackbarNotification({ message: `Requesting mount [${networkElement.nodeId}]`, options: { variant: 'info' } })); } else { dispatch(new AddSnackbarNotification({ message: `Failed to mount [${networkElement.nodeId}]`, options: { variant: 'warning' } })); } diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts index 57f036e56..d22a6c645 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/networkElementsActions.ts @@ -30,7 +30,7 @@ export class BaseAction extends Action { } /** Represents an async thunk action creator to add an element to the network elements/nodes. */ export const addNewNetworkElementAsyncActionCreator = (element: NetworkElementConnection) => async (dispatch: Dispatch) => { - const res = await connectService.createNetworkElement({ ...element }); + await connectService.createNetworkElement({ ...element }); dispatch(updateCurrentViewAsyncAction()); dispatch(new AddSnackbarNotification({ message: `Successfully added [${element.nodeId}]`, options: { variant: 'success' } })); }; @@ -39,11 +39,10 @@ export const addNewNetworkElementAsyncActionCreator = (element: NetworkElementCo 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); + if (currentConnectionStatus === 'Disconnected') { + await connectService.deleteNetworkElement(element); + } else { + await connectService.updateNetworkElement(element); } dispatch(updateCurrentViewAsyncAction()); dispatch(new AddSnackbarNotification({ message: `Successfully modified [${element.id}]`, options: { variant: 'success' } })); @@ -52,7 +51,7 @@ export const editNetworkElementAsyncActionCreator = (element: UpdateNetworkEleme /** Represents an async thunk action creator to delete an element from network elements/nodes. */ export const removeNetworkElementAsyncActionCreator = (element: UpdateNetworkElement) => async (dispatch: Dispatch) => { - const res = await connectService.deleteNetworkElement(element); + await connectService.deleteNetworkElement(element); await dispatch(unmountNetworkElementAsyncActionCreator(element && element.id)); await dispatch(updateCurrentViewAsyncAction()); }; diff --git a/sdnr/wt/odlux/apps/connectApp/src/actions/tlsKeyActions.ts b/sdnr/wt/odlux/apps/connectApp/src/actions/tlsKeyActions.ts index 1da16d9ad..65d23c439 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/actions/tlsKeyActions.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/actions/tlsKeyActions.ts @@ -17,7 +17,6 @@ */ import { Action } from '../../../../framework/src/flux/action'; import { Dispatch } from '../../../../framework/src/flux/store'; -import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import { TlsKeys } from '../models/networkElementConnection'; import { connectService } from '../services/connectService'; @@ -36,14 +35,14 @@ export class LoadAllTlsKeyListAction extends BaseAction { } * Represents an action causing the store to get all TLS Keys. */ export class AllTlsKeyListLoadedAction extends BaseAction { - /** + /** * Initialize this instance. * * @param gets all the tlsKey list from the database. */ - constructor(public tlsList: TlsKeys[] | null, public error?: string) { - super(); - } + constructor(public tlsList: TlsKeys[] | null, public error?: string) { + super(); + } } /** @@ -51,10 +50,10 @@ export class AllTlsKeyListLoadedAction extends BaseAction { */ export const loadAllTlsKeyListAsync = () => async (dispatch: Dispatch) => { - dispatch(new LoadAllTlsKeyListAction()); - connectService.getTlsKeys().then(TlsKeyList => { - dispatch(new AllTlsKeyListLoadedAction(TlsKeyList)); - }).catch(error => { - dispatch(new AllTlsKeyListLoadedAction(null, error)); - }); + dispatch(new LoadAllTlsKeyListAction()); + connectService.getTlsKeys().then(TlsKeyList => { + dispatch(new AllTlsKeyListLoadedAction(TlsKeyList)); + }).catch(error => { + dispatch(new AllTlsKeyListLoadedAction(null, error)); + }); }; diff --git a/sdnr/wt/odlux/apps/connectApp/src/assets/icons/connectAppIcon.svg b/sdnr/wt/odlux/apps/connectApp/src/assets/icons/connectAppIcon.svg new file mode 100644 index 000000000..5aca4fae7 --- /dev/null +++ b/sdnr/wt/odlux/apps/connectApp/src/assets/icons/connectAppIcon.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx index b240b2419..6a8c92438 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/connectionStatusLog.tsx @@ -15,11 +15,13 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; -import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect'; +import React from 'react'; + +import Refresh from '@mui/icons-material/Refresh'; + +import { ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import { MaterialTable, ColumnType, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; -import Refresh from '@mui/icons-material/Refresh'; import { createConnectionStatusLogActions, createConnectionStatusLogProperties } from '../handlers/connectionStatusLogHandler'; import { NetworkElementConnectionLog } from '../models/networkElementConnectionLog'; @@ -37,36 +39,36 @@ const ConnectionStatusTable = MaterialTable as MaterialTableCtorType; type ConnectionStatusLogComponentState = { - refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode -} + refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode; +}; let initialSorted = false; -class ConnectionStatusLogComponent extends React.Component { +class ConnectionStatusLogComponent extends React.Component { constructor(props: ConnectionStatusLogComponentProps) { super(props); this.state = { - refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode.None + refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode.None, }; } render(): JSX.Element { const refreshConnectionStatusLogAction = { - icon: Refresh, tooltip: 'Refresh Connection Status Log Table',ariaLabel:'refresh', onClick: () => { + icon: Refresh, tooltip: 'Refresh Connection Status Log Table', ariaLabel:'refresh', onClick: () => { this.setState({ - refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode.RefreshConnectionStatusLogTable + refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode.RefreshConnectionStatusLogTable, }); - } + }, }; return ( <> ); - }; + } private onCloseRefreshConnectionStatusLogDialog = () => { this.setState({ - refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode.None + refreshConnectionStatusLogEditorMode: RefreshConnectionStatusLogDialogMode.None, }); - } + }; + componentDidMount() { if (!initialSorted) { initialSorted = true; - this.props.connectionStatusLogActions.onHandleExplicitRequestSort("timestamp", "desc"); + this.props.connectionStatusLogActions.onHandleExplicitRequestSort('timestamp', 'desc'); } else { this.props.connectionStatusLogActions.onRefresh(); } diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx index 5740ebda0..b0db63476 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/editNetworkElementDialog.tsx @@ -15,42 +15,43 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; import Button from '@mui/material/Button'; -import TextField from '@mui/material/TextField'; import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; -import { FormControl, InputLabel, Select, MenuItem, Typography, Radio, RadioGroup, Options, FormLabel, FormControlLabel } from '@mui/material'; -import { loadAllTlsKeyListAsync } from '../actions/tlsKeyActions'; -import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import FormControl from '@mui/material/FormControl'; +import FormControlLabel from '@mui/material/FormControlLabel'; +import InputLabel from '@mui/material/InputLabel'; +import MenuItem from '@mui/material/MenuItem'; +import Radio from '@mui/material/Radio'; +import RadioGroup from '@mui/material/RadioGroup'; +import Select from '@mui/material/Select'; +import TextField from '@mui/material/TextField'; +import Typography from '@mui/material/Typography'; -import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { removeWebUriAction } from '../actions/commonNetworkElementsActions'; +import { mountNetworkElementAsyncActionCreator, unmountNetworkElementAsyncActionCreator } from '../actions/mountedNetworkElementsActions'; import { - editNetworkElementAsyncActionCreator, - addNewNetworkElementAsyncActionCreator, - removeNetworkElementAsyncActionCreator + addNewNetworkElementAsyncActionCreator, editNetworkElementAsyncActionCreator, removeNetworkElementAsyncActionCreator, } from '../actions/networkElementsActions'; - -import { unmountNetworkElementAsyncActionCreator, mountNetworkElementAsyncActionCreator } from '../actions/mountedNetworkElementsActions'; -import { NetworkElementConnection, UpdateNetworkElement, propertyOf } from '../models/networkElementConnection'; -import { removeWebUriAction } from '../actions/commonNetworkElementsActions'; +import { loadAllTlsKeyListAsync } from '../actions/tlsKeyActions'; +import { NetworkElementConnection, propertyOf, UpdateNetworkElement } from '../models/networkElementConnection'; export enum EditNetworkElementDialogMode { - None = "none", - EditNetworkElement = "editNetworkElement", - RemoveNetworkElement = "removeNetworkElement", - AddNewNetworkElement = "addNewNetworkElement", - MountNetworkElement = "mountNetworkElement", - UnmountNetworkElement = "unmountNetworkElement", + 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)); @@ -63,12 +64,12 @@ const mapDispatch = (dispatcher: IDispatcher) => ({ editNetworkElement: async (element: UpdateNetworkElement, mountElement: NetworkElementConnection) => { const values = Object.keys(element); - console.log("edit element"); + console.log('edit element'); console.log(values); //make sure properties are there in case they get renamed - const idProperty = propertyOf("id"); - const isRequiredProperty = propertyOf("isRequired"); + const idProperty = propertyOf('id'); + const isRequiredProperty = propertyOf('isRequired'); if (values.length === 2 && values.includes(idProperty as string) && values.includes(isRequiredProperty as string)) { @@ -84,92 +85,92 @@ const mapDispatch = (dispatcher: IDispatcher) => ({ await dispatcher.dispatch(removeNetworkElementAsyncActionCreator(element)); dispatcher.dispatch(removeWebUriAction(element.id)); }, - getAvailableTlsKeys: async () => await dispatcher.dispatch(loadAllTlsKeyListAsync()), + getAvailableTlsKeys: async () => dispatcher.dispatch(loadAllTlsKeyListAsync()), }); type DialogSettings = { - dialogTitle: string, - dialogDescription: string, - applyButtonText: string, - cancelButtonText: string, - enableMountIdEditor: boolean, - enableUsernameEditor: boolean, - enableExtendedEditor: boolean, -} + 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: "", + dialogTitle: '', + dialogDescription: '', + applyButtonText: '', + cancelButtonText: '', enableMountIdEditor: false, enableUsernameEditor: false, enableExtendedEditor: false, }, [EditNetworkElementDialogMode.AddNewNetworkElement]: { - dialogTitle: "Add New Node", - dialogDescription: "Add this new node:", - applyButtonText: "Add node", - cancelButtonText: "Cancel", + dialogTitle: 'Add New Node', + dialogDescription: 'Add this new node:', + applyButtonText: 'Add node', + cancelButtonText: 'Cancel', enableMountIdEditor: true, enableUsernameEditor: true, enableExtendedEditor: true, }, [EditNetworkElementDialogMode.MountNetworkElement]: { - dialogTitle: "Mount Node", - dialogDescription: "Mount this node:", - applyButtonText: "Mount node", - cancelButtonText: "Cancel", + dialogTitle: 'Mount Node', + dialogDescription: 'Mount this node:', + applyButtonText: 'Mount node', + cancelButtonText: 'Cancel', enableMountIdEditor: false, enableUsernameEditor: false, enableExtendedEditor: false, }, [EditNetworkElementDialogMode.UnmountNetworkElement]: { - dialogTitle: "Unmount Node", - dialogDescription: "Unmount this node:", - applyButtonText: "Unmount node", - cancelButtonText: "Cancel", + dialogTitle: 'Unmount Node', + dialogDescription: 'Unmount this node:', + applyButtonText: 'Unmount node', + cancelButtonText: 'Cancel', enableMountIdEditor: false, enableUsernameEditor: false, enableExtendedEditor: false, }, [EditNetworkElementDialogMode.EditNetworkElement]: { - dialogTitle: "Modify Node", - dialogDescription: "Modify this node", - applyButtonText: "Modify", - cancelButtonText: "Cancel", + dialogTitle: 'Modify Node', + dialogDescription: 'Modify this node', + applyButtonText: 'Modify', + cancelButtonText: 'Cancel', enableMountIdEditor: false, enableUsernameEditor: true, enableExtendedEditor: false, }, [EditNetworkElementDialogMode.RemoveNetworkElement]: { - dialogTitle: "Remove Node", - dialogDescription: "Do you really want to remove this node?", - applyButtonText: "Remove node", - cancelButtonText: "Cancel", + dialogTitle: 'Remove Node', + dialogDescription: 'Do you really want to remove this node?', + applyButtonText: 'Remove node', + cancelButtonText: 'Cancel', enableMountIdEditor: false, enableUsernameEditor: false, enableExtendedEditor: false, - } -} + }, +}; type EditNetworkElementDialogComponentProps = Connect & { mode: EditNetworkElementDialogMode; initialNetworkElement: NetworkElementConnection; onClose: () => void; - radioChecked: string + radioChecked: string; }; type EditNetworkElementDialogComponentState = NetworkElementConnection & { - isNameValid: boolean, - isHostSet: boolean, - isPasswordSelected: boolean, - isTlsSelected: boolean, - radioSelected: string, - showPasswordTextField: boolean, - showTlsDropdown: boolean + isNameValid: boolean; + isHostSet: boolean; + isPasswordSelected: boolean; + isTlsSelected: boolean; + radioSelected: string; + showPasswordTextField: boolean; + showTlsDropdown: boolean; }; class EditNetworkElementDialogComponent extends React.Component { @@ -189,16 +190,17 @@ class EditNetworkElementDialogComponent extends React.Component { this.setState({ radioSelected: event.target.value, showPasswordTextField: event.target.value === 'password', - showTlsDropdown: event.target.value === 'tlsKey' + showTlsDropdown: event.target.value === 'tlsKey', }); - } + }; render(): JSX.Element { const setting = settings[this.props.mode]; @@ -215,22 +217,27 @@ class EditNetworkElementDialogComponent extends React.Component - {setting.dialogTitle} + {setting.dialogTitle} {setting.dialogDescription} - { this.setState({ nodeId: event.target.value }); }} /> + { this.setState({ nodeId: event.target.value }); }} /> {!this.state.isNameValid && Node ID cannot be empty.} - { this.setState({ host: event.target.value }); }} /> + { this.setState({ host: event.target.value }); }} /> {!this.state.isHostSet && Host/IP address cannot be empty.} - { this.setState({ port: +event.target.value }); }} /> - {setting.enableUsernameEditor && { this.setState({ username: event.target.value }); }} /> || null} + { this.setState({ port: +event.target.value }); }} /> + {setting.enableUsernameEditor && { this.setState({ username: event.target.value }); }} /> || null} {setting.enableUsernameEditor && { this.setState({ tlsKey: event.target.value as any }); }} inputProps={{ name: 'tlsKey', id: 'tlsKey' }} > - --Select tls-key-- + --Select tls-key-- {tlsKeysList.map(tlsKey => ({tlsKey.key}))} @@ -283,7 +290,7 @@ class EditNetworkElementDialogComponent extends React.Component { if (e.target.value == 'password') { - this.setState({ isPasswordSelected: true, isTlsSelected: false }) + this.setState({ isPasswordSelected: true, isTlsSelected: false }); } else if (e.target.value == 'tlsKey') { - this.setState({ isPasswordSelected: false, isTlsSelected: true }) + this.setState({ isPasswordSelected: false, isTlsSelected: true }); } }; private onApply = (element: NetworkElementConnection) => { - this.props.onClose && this.props.onClose(); + if (this.props.onClose) this.props.onClose(); let updateElement: UpdateNetworkElement = { - id: this.state.nodeId - } + id: this.state.nodeId, + }; if (this.state.isPasswordSelected) { - element.tlsKey = '' - } - else if (this.state.isTlsSelected) { //check here - element.password = '' + element.tlsKey = ''; + } else if (this.state.isTlsSelected) { //check here + element.password = ''; } switch (this.props.mode) { case EditNetworkElementDialogMode.AddNewNetworkElement: - element && this.props.addNewNetworkElement(element); + if (element) this.props.addNewNetworkElement(element); this.setState({ radioSelected: '', isPasswordSelected: true, }); break; case EditNetworkElementDialogMode.MountNetworkElement: - element && this.props.mountNetworkElement(element); + if (element) this.props.mountNetworkElement(element); break; case EditNetworkElementDialogMode.UnmountNetworkElement: - element && this.props.unmountNetworkElement(element); + if (element) this.props.unmountNetworkElement(element); break; case EditNetworkElementDialogMode.EditNetworkElement: if (this.props.initialNetworkElement.isRequired !== this.state.isRequired) @@ -358,13 +364,13 @@ class EditNetworkElementDialogComponent extends React.Component { - this.props.onClose && this.props.onClose(); + if (this.props.onClose) this.props.onClose(); this.setState({ password: '', username: '', tlsKey: '', radioSelected: '' }); this.resetRequieredFields(); - } + }; private resetRequieredFields() { this.setState({ isNameValid: true, isHostSet: true }); @@ -402,15 +408,16 @@ class EditNetworkElementDialogComponent extends React.Component ({ - }); - - - const InfoElementTable = MaterialTable as MaterialTableCtorType; - - 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 Node", - dialogDescription: "", - cancelButtonText: "OK", - } - } - - type InfoNetworkElementDialogComponentProps = Connect & { - mode: InfoNetworkElementDialogMode; - initialNetworkElement: NetworkElementConnection; - onClose: () => void; - }; - - type InfoNetworkElementDialogComponentState = NetworkElementConnection; - - class InfoNetworkElementDialogComponent extends React.Component { - 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 yangFeatures = this.props.state.connect.elementFeatureInfo.elementFeatureInfo; - 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) { - let moduleName = capabilty.substring(indexModule + 1); - let ModuleFeaturesList; - for(let index = 0; index < yangFeatures.length; index++) { - if(yangFeatures[index].name == moduleName) { - ModuleFeaturesList = yangFeatures[index].feature? yangFeatures[index].feature : null; - break; - } - } - const featuresListCommaSeparated= ModuleFeaturesList? ModuleFeaturesList.toString() : "" - let featuresList = featuresListCommaSeparated.replace(',',', '); - - yangCapabilities.push({ - module: moduleName, - revision: capabilty.substring(indexRevision + 9, indexRevision + 19), - features: featuresList - }); - } - }); - - yangCapabilities = yangCapabilities.sort((a,b) => a.module === b.module ? 0 : a.module > b.module ? 1 : -1); - - return ( - <> - - {`${setting.dialogTitle}: "${this.state.nodeId}"`} - { - return ( - - ) - } - }, - { property: "features", title: "Features", type: ColumnType.text, width:500 }, - ]} idProperty="id" rows={yangCapabilities} > - - - - - - - ) - } - - 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 +import * as React from 'react'; + +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogTitle from '@mui/material/DialogTitle'; + +import { ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { 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 = () => ({ +}); + +const InfoElementTable = MaterialTable as MaterialTableCtorType; + +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 Node', + dialogDescription: '', + cancelButtonText: 'OK', + }, +}; + +type InfoNetworkElementDialogComponentProps = Connect & { + mode: InfoNetworkElementDialogMode; + initialNetworkElement: NetworkElementConnection; + onClose: () => void; +}; + +type InfoNetworkElementDialogComponentState = NetworkElementConnection; + +class InfoNetworkElementDialogComponent extends React.Component { + 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 yangFeatures = this.props.state.connect.elementFeatureInfo.elementFeatureInfo; + 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) { + let moduleName = capabilty.substring(indexModule + 1); + let ModuleFeaturesList; + for (let index = 0; index < yangFeatures.length; index++) { + if (yangFeatures[index].name == moduleName) { + ModuleFeaturesList = yangFeatures[index].feature ? yangFeatures[index].feature : null; + break; + } + } + const featuresListCommaSeparated = ModuleFeaturesList ? ModuleFeaturesList.toString() : ''; + let featuresList = featuresListCommaSeparated.replace(',', ', '); + + yangCapabilities.push({ + module: moduleName, + revision: capabilty.substring(indexRevision + 9, indexRevision + 19), + features: featuresList, + }); + } + }); + + yangCapabilities = yangCapabilities.sort((a, b) => a.module === b.module ? 0 : a.module > b.module ? 1 : -1); + + return ( + <> + + {`${setting.dialogTitle}: "${this.state.nodeId}"`} + { + return ( + + ); + }, + }, + { property: 'features', title: 'Features', type: ColumnType.text, width: 500 }, + ]} idProperty="id" rows={yangCapabilities} > + + + + + + + ); + } + + private onCancel = () => { + this.props.onClose(); + }; + + static getDerivedStateFromProps(props: InfoNetworkElementDialogComponentProps, state: InfoNetworkElementDialogComponentState & { initialNetworkElement: NetworkElementConnection }): InfoNetworkElementDialogComponentState & { initialNetworkElement: NetworkElementConnection } { + let returnState = state; + if (props.initialNetworkElement !== state.initialNetworkElement) { + returnState = { + ...state, + ...props.initialNetworkElement, + initialNetworkElement: props.initialNetworkElement, + }; + } + return returnState; + } +} + +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 index 67fdef69d..1ce8f0c3b 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/networkElements.tsx @@ -15,39 +15,37 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; -import { Theme } from '@mui/material/styles'; - -import { WithStyles } from '@mui/styles'; -import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; +import React from 'react'; import AddIcon from '@mui/icons-material/Add'; -import Refresh from '@mui/icons-material/Refresh'; +import ComputerIcon from '@mui/icons-material/Computer'; +import EditIcon from '@mui/icons-material/Edit'; +import Info from '@mui/icons-material/Info'; import LinkIcon from '@mui/icons-material/Link'; import LinkOffIcon from '@mui/icons-material/LinkOff'; +import Refresh from '@mui/icons-material/Refresh'; import RemoveIcon from '@mui/icons-material/RemoveCircleOutline'; -import EditIcon from '@mui/icons-material/Edit'; -import Info from '@mui/icons-material/Info'; -import ComputerIcon from '@mui/icons-material/Computer'; -import { MenuItem, Divider, Typography } from '@mui/material'; +import { Divider, MenuItem, Typography } from '@mui/material'; +import { Theme } from '@mui/material/styles'; +import { WithStyles } from '@mui/styles'; +import createStyles from '@mui/styles/createStyles'; +import withStyles from '@mui/styles/withStyles'; -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 { ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { getAccessPolicyByUrl } from '../../../../framework/src/services/restService'; +import { loadAllInfoElementAsync, loadAllInfoElementFeaturesAsync } from '../actions/infoNetworkElementActions'; import { createNetworkElementsActions, createNetworkElementsProperties } from '../handlers/networkElementsHandler'; - import { NetworkElementConnection } from '../models/networkElementConnection'; import { ModuleSet, TopologyNode } from '../models/topologyNetconf'; -import EditNetworkElementDialog, { EditNetworkElementDialogMode } from './editNetworkElementDialog'; -import RefreshNetworkElementsDialog, { RefreshNetworkElementsDialogMode } from './refreshNetworkElementsDialog'; +import { connectService } from '../services/connectService'; +import EditNetworkElementDialog, { EditNetworkElementDialogMode } from './editNetworkElementDialog'; import InfoNetworkElementDialog, { InfoNetworkElementDialogMode } from './infoNetworkElementDialog'; -import { loadAllInfoElementAsync, loadAllInfoElementFeaturesAsync } from '../actions/infoNetworkElementActions'; -import { connectService } from '../services/connectService'; -import { getAccessPolicyByUrl } from '../../../../framework/src/services/restService'; +import RefreshNetworkElementsDialog, { RefreshNetworkElementsDialogMode } from './refreshNetworkElementsDialog'; const styles = (theme: Theme) => createStyles({ connectionStatusConnected: { @@ -61,17 +59,17 @@ const styles = (theme: Theme) => createStyles({ }, button: { margin: 0, - padding: "6px 6px", - minWidth: 'unset' + padding: '6px 6px', + minWidth: 'unset', }, spacer: { marginLeft: theme.spacing(1), marginRight: theme.spacing(1), - display: "inline" - } + display: 'inline', + }, }); -type GetStatelessComponentProps = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any +type GetStatelessComponentProps = T extends (props: infer P & { children?: React.ReactNode }) => any ? P : any; const MenuItemExt: React.FC> = (props) => { const [disabled, setDisabled] = React.useState(true); const onMouseDown = (ev: React.MouseEvent) => { @@ -95,21 +93,21 @@ const mapProps = (state: IApplicationStoreState) => ({ 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)), - networkElementFeaturesInfo: async (nodeId: string) => await dispatcher.dispatch(loadAllInfoElementFeaturesAsync(nodeId)) + networkElementInfo: async (nodeId: string) => dispatcher.dispatch(loadAllInfoElementAsync(nodeId)), + networkElementFeaturesInfo: async (nodeId: string) => dispatcher.dispatch(loadAllInfoElementFeaturesAsync(nodeId)), }); type NetworkElementsListComponentProps = WithStyles & Connect; type NetworkElementsListComponentState = { - networkElementToEdit: NetworkElementConnection, - networkElementEditorMode: EditNetworkElementDialogMode, - refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode, - infoNetworkElementEditorMode: InfoNetworkElementDialogMode, - elementInfo: TopologyNode | null, - elementInfoFeature: ModuleSet | null -} + networkElementToEdit: NetworkElementConnection; + networkElementEditorMode: EditNetworkElementDialogMode; + refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode; + infoNetworkElementEditorMode: InfoNetworkElementDialogMode; + elementInfo: TopologyNode | null; + elementInfoFeature: ModuleSet | null; +}; -const emptyRequireNetworkElement: NetworkElementConnection = { id: "", nodeId: "", host: "", port: 830, status: "Disconnected", isRequired: true }; +const emptyRequireNetworkElement: NetworkElementConnection = { id: '', nodeId: '', host: '', port: 830, status: 'Disconnected', isRequired: true }; let initialSorted = false; const NetworkElementTable = MaterialTable as MaterialTableCtorType; @@ -124,7 +122,7 @@ export class NetworkElementsListComponent extends React.Component this.onOpenMountdNetworkElementsDialog(event, rowData)} disabled={!canMount} >Mount, - this.onOpenUnmountdNetworkElementsDialog(event, rowData)} disabled={!canMount} >Unmount, + this.onOpenMountdNetworkElementsDialog(event, rowData)} disabled={!canMount} >Mount, + this.onOpenUnmountdNetworkElementsDialog(event, rowData)} disabled={!canMount} >Unmount, , - this.onOpenInfoNetworkElementDialog(event, rowData)} disabled={rowData.status !== "Connected"} >Info, - this.onOpenEditNetworkElementDialog(event, rowData)}>Edit, - this.onOpenRemoveNetworkElementDialog(event, rowData)} >Remove, + this.onOpenInfoNetworkElementDialog(event, rowData)} disabled={rowData.status !== 'Connected'} >Info, + this.onOpenEditNetworkElementDialog(event, rowData)}>Edit, + this.onOpenRemoveNetworkElementDialog(event, rowData)} >Remove, , - this.props.navigateToApplication("inventory", rowData.nodeId)}>Inventory, + this.props.navigateToApplication('inventory', rowData.nodeId)}>Inventory, , - this.props.navigateToApplication("fault", rowData.nodeId)} >Fault, - this.props.navigateToApplication("configuration", rowData.nodeId)} disabled={rowData.status === "Connecting" || rowData.status === "Disconnected" || !configuration}>Configure, - this.props.navigateToApplication("accounting", rowData.nodeId)} disabled={true}>Accounting, - this.props.navigateToApplication("performanceHistory", rowData.nodeId)}>Performance, - this.props.navigateToApplication("security", rowData.nodeId)} disabled={true} >Security, + this.props.navigateToApplication('fault', rowData.nodeId)} >Fault, + this.props.navigateToApplication('configuration', rowData.nodeId)} disabled={rowData.status === 'Connecting' || rowData.status === 'Disconnected' || !configuration}>Configure, + this.props.navigateToApplication('accounting', rowData.nodeId)} disabled={true}>Accounting, + this.props.navigateToApplication('performanceHistory', rowData.nodeId)}>Performance, + this.props.navigateToApplication('security', rowData.nodeId)} disabled={true} >Security, ]; if (rowData.weburi) { // add an icon for gui cuttrough, if weburi is available - return [ window.open(rowData.weburi, "_blank")} >Web Client].concat(buttonArray) + return [ window.open(rowData.weburi, '_blank')} >Web Client].concat(buttonArray); } else { return buttonArray; } @@ -162,13 +160,13 @@ export class NetworkElementsListComponent extends React.Component 0) { - savedRadio = 'password' + savedRadio = 'password'; } else if (this.state.networkElementToEdit.tlsKey && this.state.networkElementToEdit.tlsKey.length > 0) { - savedRadio = 'tlsKey' + savedRadio = 'tlsKey'; } // const mountUri = rowData.id && connectService.getNetworkElementUri(rowData.id); @@ -177,32 +175,32 @@ export class NetworkElementsListComponent extends React.Component { + icon: AddIcon, tooltip: 'Add node', ariaLabel: 'add-element', onClick: () => { this.setState({ networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement, networkElementToEdit: emptyRequireNetworkElement, }); - } + }, }; const refreshNetworkElementsAction = { icon: Refresh, tooltip: 'Refresh table', ariaLabel: 'refresh', onClick: () => { this.setState({ - refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.RefreshNetworkElementsTable + refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.RefreshNetworkElementsTable, }); - } + }, }; return <> { return this.getContextMenu(rowData); @@ -224,12 +222,12 @@ export class NetworkElementsListComponent extends React.Component ; - }; + } public componentDidMount() { if (!initialSorted) { initialSorted = true; - this.props.networkElementsActions.onHandleRequestSort("node-id"); + this.props.networkElementsActions.onHandleRequestSort('node-id'); } else { this.props.networkElementsActions.onRefresh(); } @@ -238,23 +236,23 @@ export class NetworkElementsListComponent extends React.Component, element: NetworkElementConnection) => { this.setState({ networkElementToEdit: element, - networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement + networkElementEditorMode: EditNetworkElementDialogMode.AddNewNetworkElement, }); - } + }; private onOpenRemoveNetworkElementDialog = (event: React.MouseEvent, element: NetworkElementConnection) => { this.setState({ networkElementToEdit: element, - networkElementEditorMode: EditNetworkElementDialogMode.RemoveNetworkElement + networkElementEditorMode: EditNetworkElementDialogMode.RemoveNetworkElement, }); - } + }; private onOpenEditNetworkElementDialog = (event: React.MouseEvent, element: NetworkElementConnection) => { - let radioSaved; - if (element.password && element.password.length > 0) - radioSaved = 'password' - else if (element.tlsKey && element.tlsKey.length > 0) - radioSaved = 'tlsKey' + //let radioSaved; + //if (element.password && element.password.length > 0) + // radioSaved = 'password'; + //else if (element.tlsKey && element.tlsKey.length > 0) + // radioSaved = 'tlsKey'; this.setState({ networkElementToEdit: { nodeId: element.nodeId, @@ -263,25 +261,25 @@ export class NetworkElementsListComponent extends React.Component, element: NetworkElementConnection) => { this.setState({ networkElementToEdit: element, - networkElementEditorMode: EditNetworkElementDialogMode.UnmountNetworkElement + networkElementEditorMode: EditNetworkElementDialogMode.UnmountNetworkElement, }); - } + }; private onOpenMountdNetworkElementsDialog = (event: React.MouseEvent, element: NetworkElementConnection) => { this.setState({ networkElementToEdit: element, - networkElementEditorMode: EditNetworkElementDialogMode.MountNetworkElement + networkElementEditorMode: EditNetworkElementDialogMode.MountNetworkElement, }); - } + }; private onOpenInfoNetworkElementDialog = (event: React.MouseEvent, element: NetworkElementConnection) => { this.props.networkElementInfo(element.nodeId); @@ -290,25 +288,27 @@ export class NetworkElementsListComponent extends React.Component { this.setState({ networkElementEditorMode: EditNetworkElementDialogMode.None, networkElementToEdit: emptyRequireNetworkElement, }); - } + }; + private onCloseInfoNetworkElementDialog = () => { this.setState({ infoNetworkElementEditorMode: InfoNetworkElementDialogMode.None, networkElementToEdit: emptyRequireNetworkElement, }); - } + }; + private onCloseRefreshNetworkElementsDialog = () => { this.setState({ - refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None + refreshNetworkElementsEditorMode: RefreshNetworkElementsDialogMode.None, }); - } + }; } export const NetworkElementsList = withStyles(styles)(connect(mapProps, mapDispatch)(NetworkElementsListComponent)); diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/refreshConnectionStatusLogDialog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/refreshConnectionStatusLogDialog.tsx index c09f59b40..a4aea7f82 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/refreshConnectionStatusLogDialog.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/refreshConnectionStatusLogDialog.tsx @@ -15,7 +15,7 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; import Button from '@mui/material/Button'; import Dialog from '@mui/material/Dialog'; @@ -24,78 +24,75 @@ import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; -import { connectionStatusLogReloadAction } from '../handlers/connectionStatusLogHandler'; -import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { connectionStatusLogReloadAction } from '../handlers/connectionStatusLogHandler'; import { ConnectionStatusLogType } from '../models/connectionStatusLog'; export enum RefreshConnectionStatusLogDialogMode { - None = "none", - RefreshConnectionStatusLogTable = "RefreshConnectionStatusLogTable", + None = 'none', + RefreshConnectionStatusLogTable = 'RefreshConnectionStatusLogTable', } const mapDispatch = (dispatcher: IDispatcher) => ({ - refreshConnectionStatusLog: () => dispatcher.dispatch(connectionStatusLogReloadAction) + refreshConnectionStatusLog: () => dispatcher.dispatch(connectionStatusLogReloadAction), }); type DialogSettings = { - dialogTitle: string, - dialogDescription: string, - applyButtonText: string, - cancelButtonText: string, - enableMountIdEditor: boolean, - enableUsernameEditor: boolean, - enableExtendedEditor: boolean, -} + dialogTitle: string; + dialogDescription: string; + applyButtonText: string; + cancelButtonText: string; + enableMountIdEditor: boolean; + enableUsernameEditor: boolean; + enableExtendedEditor: boolean; +}; const settings: { [key: string]: DialogSettings } = { [RefreshConnectionStatusLogDialogMode.None]: { - dialogTitle: "", - dialogDescription: "", - applyButtonText: "", - cancelButtonText: "", + dialogTitle: '', + dialogDescription: '', + applyButtonText: '', + cancelButtonText: '', enableMountIdEditor: false, enableUsernameEditor: false, enableExtendedEditor: false, }, [RefreshConnectionStatusLogDialogMode.RefreshConnectionStatusLogTable]: { - dialogTitle: "Do you want to refresh the Connection Status Log table?", - dialogDescription: "", - applyButtonText: "Yes", - cancelButtonText: "Cancel", + dialogTitle: 'Do you want to refresh the Connection Status Log table?', + dialogDescription: '', + applyButtonText: 'Yes', + cancelButtonText: 'Cancel', enableMountIdEditor: true, enableUsernameEditor: true, enableExtendedEditor: true, - } -} + }, +}; type RefreshConnectionStatusLogDialogComponentProps = Connect & { mode: RefreshConnectionStatusLogDialogMode; onClose: () => void; }; -type RefreshConnectionStatusLogDialogComponentState = ConnectionStatusLogType & { isNameValid: boolean, isHostSet: boolean }; +type RefreshConnectionStatusLogDialogComponentState = ConnectionStatusLogType & { isNameValid: boolean; isHostSet: boolean }; class RefreshConnectionStatusLogDialogComponent extends React.Component { - constructor(props: RefreshConnectionStatusLogDialogComponentProps) { - super(props); - } render(): JSX.Element { const setting = settings[this.props.mode]; return ( - {setting.dialogTitle} + {setting.dialogTitle} {setting.dialogDescription} - - @@ -110,7 +107,7 @@ class RefreshConnectionStatusLogDialogComponent extends React.Component { this.props.onClose(); - } + }; } export const RefreshConnectionStatusLogDialog = connect(undefined, mapDispatch)(RefreshConnectionStatusLogDialogComponent); diff --git a/sdnr/wt/odlux/apps/connectApp/src/components/refreshNetworkElementsDialog.tsx b/sdnr/wt/odlux/apps/connectApp/src/components/refreshNetworkElementsDialog.tsx index abf593882..e41fd27aa 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/components/refreshNetworkElementsDialog.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/components/refreshNetworkElementsDialog.tsx @@ -15,7 +15,7 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; import Button from '@mui/material/Button'; import Dialog from '@mui/material/Dialog'; @@ -24,78 +24,75 @@ import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; -import { networkElementsReloadAction } from '../handlers/networkElementsHandler'; -import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { networkElementsReloadAction } from '../handlers/networkElementsHandler'; import { NetworkElementConnection } from '../models/networkElementConnection'; export enum RefreshNetworkElementsDialogMode { - None = "none", - RefreshNetworkElementsTable = "RefreshNetworkElementsTable", + None = 'none', + RefreshNetworkElementsTable = 'RefreshNetworkElementsTable', } const mapDispatch = (dispatcher: IDispatcher) => ({ - refreshNetworkElement: () => dispatcher.dispatch(networkElementsReloadAction) + refreshNetworkElement: () => dispatcher.dispatch(networkElementsReloadAction), }); type DialogSettings = { - dialogTitle: string, - dialogDescription: string, - applyButtonText: string, - cancelButtonText: string, - enableMountIdEditor: boolean, - enableUsernameEditor: boolean, - enableExtendedEditor: boolean, -} + dialogTitle: string; + dialogDescription: string; + applyButtonText: string; + cancelButtonText: string; + enableMountIdEditor: boolean; + enableUsernameEditor: boolean; + enableExtendedEditor: boolean; +}; const settings: { [key: string]: DialogSettings } = { [RefreshNetworkElementsDialogMode.None]: { - dialogTitle: "", - dialogDescription: "", - applyButtonText: "", - cancelButtonText: "", + dialogTitle: '', + dialogDescription: '', + applyButtonText: '', + cancelButtonText: '', enableMountIdEditor: false, enableUsernameEditor: false, enableExtendedEditor: false, }, [RefreshNetworkElementsDialogMode.RefreshNetworkElementsTable]: { - dialogTitle: "Do you want to refresh the nodes table?", - dialogDescription: "", - applyButtonText: "Yes", - cancelButtonText: "Cancel", + dialogTitle: 'Do you want to refresh the nodes table?', + dialogDescription: '', + applyButtonText: 'Yes', + cancelButtonText: 'Cancel', enableMountIdEditor: true, enableUsernameEditor: true, enableExtendedEditor: true, - } -} + }, +}; type RefreshNetworkElementsDialogComponentProps = Connect & { mode: RefreshNetworkElementsDialogMode; onClose: () => void; }; -type RefreshNetworkElementsDialogComponentState = NetworkElementConnection & { isNameValid: boolean, isHostSet: boolean }; +type RefreshNetworkElementsDialogComponentState = NetworkElementConnection & { isNameValid: boolean; isHostSet: boolean }; class RefreshNetworkElementsDialogComponent extends React.Component { - constructor(props: RefreshNetworkElementsDialogComponentProps) { - super(props); - } render(): JSX.Element { const setting = settings[this.props.mode]; return ( - {setting.dialogTitle} + {setting.dialogTitle} {setting.dialogDescription} - - @@ -110,7 +107,7 @@ class RefreshNetworkElementsDialogComponent extends React.Component { this.props.onClose(); - } + }; } export const RefreshNetworkElementsDialog = connect(undefined, mapDispatch)(RefreshNetworkElementsDialogComponent); diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts index 6a1825224..b386dcdef 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectAppRootHandler.ts @@ -18,12 +18,13 @@ 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, IInfoNetworkElementFeaturesState, infoNetworkElementFeaturesActionHandler } from './infoNetworkElementHandler'; -import { SetPanelAction, AddWebUriList, RemoveWebUri, SetWeburiSearchBusy } from '../actions/commonNetworkElementsActions'; -import { PanelId } from '../models/panelId'; + +import { AddWebUriList, RemoveWebUri, SetPanelAction } from '../actions/commonNetworkElementsActions'; import { guiCutThrough } from '../models/guiCutTrough'; +import { PanelId } from '../models/panelId'; +import { connectionStatusLogActionHandler, IConnectionStatusLogState } from './connectionStatusLogHandler'; +import { IInfoNetworkElementFeaturesState, IInfoNetworkElementsState, infoNetworkElementFeaturesActionHandler, infoNetworkElementsActionHandler } from './infoNetworkElementHandler'; +import { INetworkElementsState, networkElementsActionHandler } from './networkElementsHandler'; import { availableTlsKeysActionHandler, IAvailableTlsKeysState } from './tlsKeyHandler'; export interface IConnectAppStoreState { @@ -33,7 +34,7 @@ export interface IConnectAppStoreState { elementInfo: IInfoNetworkElementsState; elementFeatureInfo: IInfoNetworkElementFeaturesState; guiCutThrough: guiCutThroughState; - availableTlsKeys: IAvailableTlsKeysState + availableTlsKeys: IAvailableTlsKeysState; } const currentOpenPanelHandler: IActionHandler = (state = null, action) => { @@ -41,7 +42,7 @@ const currentOpenPanelHandler: IActionHandler = (state = null, action) state = action.panelId; } return state; -} +}; interface guiCutThroughState { searchedElements: guiCutThrough[]; @@ -62,12 +63,12 @@ const guiCutThroughHandler: IActionHandler = (state = { sear if (action.newlySearchedElements) { action.newlySearchedElements.forEach(item => { notSearchedElements = notSearchedElements.filter(id => id !== item); - }) + }); } searchedElements = state.searchedElements.concat(action.searchedElements); - state = { searchedElements: searchedElements, notSearchedElements: notSearchedElements, unsupportedElements: unsupportedElements } + state = { searchedElements: searchedElements, notSearchedElements: notSearchedElements, unsupportedElements: unsupportedElements }; } else if (action instanceof RemoveWebUri) { const nodeId = action.element; @@ -77,11 +78,11 @@ const guiCutThroughHandler: IActionHandler = (state = { sear state = { notSearchedElements: knownElements, searchedElements: webUris, unsupportedElements: unsupportedElement }; } return state; -} +}; declare module '../../../../framework/src/store/applicationStore' { interface IApplicationStoreState { - connect: IConnectAppStoreState + connect: IConnectAppStoreState; } } @@ -92,7 +93,7 @@ const actionHandlers = { elementInfo: infoNetworkElementsActionHandler, elementFeatureInfo: infoNetworkElementFeaturesActionHandler, guiCutThrough: guiCutThroughHandler, - availableTlsKeys: availableTlsKeysActionHandler + availableTlsKeys: availableTlsKeysActionHandler, }; export const connectAppRootHandler = combineActionHandler(actionHandlers); diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusLogHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusLogHandler.ts index 6863ec33b..264b6c198 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusLogHandler.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/connectionStatusLogHandler.ts @@ -15,10 +15,11 @@ * the License. * ============LICENSE_END========================================================================== */ -import { createExternal,IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; +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 { } // create eleactic search material data fetch handler diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/infoNetworkElementHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/infoNetworkElementHandler.ts index 3e2d1cec1..692e63a5c 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/handlers/infoNetworkElementHandler.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/infoNetworkElementHandler.ts @@ -15,79 +15,78 @@ * the License. * ============LICENSE_END========================================================================== */ - import { IActionHandler } from '../../../../framework/src/flux/action'; +import { IActionHandler } from '../../../../framework/src/flux/action'; - import { AllElementInfoLoadedAction, AllElementInfoFeatureLoadedAction, LoadAllElementInfoAction } from '../actions/infoNetworkElementActions'; +import { AllElementInfoFeatureLoadedAction, AllElementInfoLoadedAction, LoadAllElementInfoAction } from '../actions/infoNetworkElementActions'; +import { Module, TopologyNode } from '../models/topologyNetconf'; - import { Module, TopologyNode } from '../models/topologyNetconf'; +export interface IInfoNetworkElementsState { + elementInfo: TopologyNode; + busy: boolean; +} - export interface IInfoNetworkElementsState { - elementInfo: TopologyNode; - busy: boolean; - } +export interface IInfoNetworkElementFeaturesState { + elementFeatureInfo: Module[]; + busy: boolean; +} - export interface IInfoNetworkElementFeaturesState { - elementFeatureInfo: Module[]; - busy: boolean; - } +const infoNetworkElementsStateInit: IInfoNetworkElementsState = { + elementInfo: { + 'node-id': '', + 'netconf-node-topology:available-capabilities': { + 'available-capability': [], + }, + }, + busy: false, +}; - const infoNetworkElementsStateInit: IInfoNetworkElementsState = { - elementInfo: { - "node-id": "", - "netconf-node-topology:available-capabilities": { - "available-capability": [] - } - }, - busy: false - }; +const infoNetworkElementFeaturesStateInit: IInfoNetworkElementFeaturesState = { + elementFeatureInfo: [], + busy: false, +}; - const infoNetworkElementFeaturesStateInit: IInfoNetworkElementFeaturesState = { - elementFeatureInfo: [], - busy: false - }; +export const infoNetworkElementsActionHandler: IActionHandler = (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; +}; - export const infoNetworkElementsActionHandler: IActionHandler = (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; - }; - - export const infoNetworkElementFeaturesActionHandler: IActionHandler = (state = infoNetworkElementFeaturesStateInit, action) => { - if (action instanceof LoadAllElementInfoAction) { - state = { - ...state, - busy: true - }; - } else if (action instanceof AllElementInfoFeatureLoadedAction) { - if (!action.error && action.elementFeatureInfo) { - state = { - ...state, - elementFeatureInfo: action.elementFeatureInfo, - busy: false - }; - } else { - state = { - ...state, - busy: false - }; - } - } - return state; - }; \ No newline at end of file +export const infoNetworkElementFeaturesActionHandler: IActionHandler = (state = infoNetworkElementFeaturesStateInit, action) => { + if (action instanceof LoadAllElementInfoAction) { + state = { + ...state, + busy: true, + }; + } else if (action instanceof AllElementInfoFeatureLoadedAction) { + if (!action.error && action.elementFeatureInfo) { + state = { + ...state, + elementFeatureInfo: action.elementFeatureInfo, + 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 index b74a39427..42d2824b9 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/networkElementsHandler.ts @@ -16,8 +16,8 @@ * ============LICENSE_END========================================================================== */ import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; -import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch'; import { getAccessPolicyByUrl } from '../../../../framework/src/services/restService'; +import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch'; import { NetworkElementConnection } from '../models/networkElementConnection'; import { connectService } from '../services/connectService'; @@ -32,7 +32,7 @@ export const { createActions: createNetworkElementsActions, createProperties: createNetworkElementsProperties, reloadAction: networkElementsReloadAction, - + // set value action, to change a value } = createExternal(networkElementsSearchHandler, appState => { @@ -42,20 +42,19 @@ export const { appState.connect.networkElements.rows.forEach(element => { - if (element.status === "Connected") { + if (element.status === 'Connected') { const webUri = webUris.find(item => item.id === element.id as string); if (webUri) { element.weburi = webUri.weburi; element.isWebUriUnreachable = false; - } - else { - element.isWebUriUnreachable = true + } else { + element.isWebUriUnreachable = true; } } }); } - return appState.connect.networkElements + return appState.connect.networkElements; }, (ne) => { if (!ne || !ne.id) return true; const neUrl = connectService.getNetworkElementUri(ne.id); diff --git a/sdnr/wt/odlux/apps/connectApp/src/handlers/tlsKeyHandler.ts b/sdnr/wt/odlux/apps/connectApp/src/handlers/tlsKeyHandler.ts index 326b3cc8e..20badcba0 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/handlers/tlsKeyHandler.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/handlers/tlsKeyHandler.ts @@ -34,7 +34,7 @@ export const availableTlsKeysActionHandler: IActionHandler | null = null; - const mapProps = (state: IApplicationStoreState) => ({ networkElementDashboardProperties: createNetworkElementsProperties(state), @@ -48,24 +46,25 @@ const mapDisp = (dispatcher: IDispatcher) => ({ }); const ConnectApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ status?: string }> & Connect) => { + + // TODO: move into useEffect! if (currentStatus !== props.match.params.status) { currentStatus = props.match.params.status || undefined; window.setTimeout(() => { if (currentStatus) { - props.setCurrentPanel("NetworkElements"); - props.networkElementsDashboardActions.onFilterChanged("status", currentStatus); + props.setCurrentPanel('NetworkElements'); + props.networkElementsDashboardActions.onFilterChanged('status', currentStatus); if (!props.networkElementDashboardProperties.showFilter) { props.networkElementsDashboardActions.onToggleFilter(false); props.networkElementsDashboardActions.onRefresh(); - } - else + } else props.networkElementsDashboardActions.onRefresh(); } }); } return ( - ) + ); }); @@ -79,19 +78,19 @@ const App = withRouter((props: RouteComponentProps) => ( export function register() { const applicationApi = applicationManager.registerApplication({ - name: "connect", - icon: faPlug, + name: 'connect', + icon: appIcon, rootComponent: App, rootActionHandler: connectAppRootHandler, - menuEntry: "Connect" + menuEntry: 'Connect', }); // subscribe to the websocket notifications - subscribe(["object-creation-notification", "object-deletion-notification", "attribute-value-changed-notification"], (msg => { + subscribe(['object-creation-notification', 'object-deletion-notification', 'attribute-value-changed-notification'], (msg => { const store = applicationApi.applicationStore; - if (msg && msg.type.type === "object-creation-notification" && store) { + if (msg && msg.type.type === 'object-creation-notification' && store) { store.dispatch(new AddSnackbarNotification({ message: `Adding node [${msg.data['object-id-ref']}]`, options: { variant: 'info' } })); - } else if (msg && (msg.type.type === "object-deletion-notification" || msg.type.type === "attribute-value-changed-notification") && store) { + } else if (msg && (msg.type.type === 'object-deletion-notification' || msg.type.type === 'attribute-value-changed-notification') && store) { store.dispatch(new AddSnackbarNotification({ message: `Updating node [${msg.data['object-id-ref']}]`, options: { variant: 'info' } })); } if (store) { diff --git a/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts b/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts index 08cc58056..1d74f859a 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts +++ b/sdnr/wt/odlux/apps/connectApp/src/services/connectService.ts @@ -18,7 +18,7 @@ import { requestRest } from '../../../../framework/src/services/restService'; import { NetworkElementConnection, ConnectionStatus, UpdateNetworkElement } from '../models/networkElementConnection'; -import { TlsKeys } from '../models/networkElementConnection' +import { TlsKeys } from '../models/networkElementConnection'; import { convertPropertyNames, replaceUpperCase } from '../../../../framework/src/utilities/yangHelper'; import { Result } from '../../../../framework/src/models/elasticSearch'; @@ -30,17 +30,20 @@ import { guiCutThrough } from '../models/guiCutTrough'; */ class ConnectService { public getNetworkElementUri = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId; - public getNetworkElementConnectDataProviderUri = (operation: "create" | "update" | "delete") => `/rests/operations/data-provider:${operation}-network-element-connection`; + + public getNetworkElementConnectDataProviderUri = (operation: 'create' | 'update' | 'delete') => `/rests/operations/data-provider:${operation}-network-element-connection`; + public getAllWebUriExtensionsForNetworkElementListUri = (nodeId: string) => this.getNetworkElementUri(nodeId) + '/yang-ext:mount/core-model:network-element'; - public getNetworkElementYangLibraryFeature = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId + '/yang-ext:mount/ietf-yang-library:yang-library?content=nonconfig' + + public getNetworkElementYangLibraryFeature = (nodeId: string) => '/rests/data/network-topology:network-topology/topology=topology-netconf/node=' + nodeId + '/yang-ext:mount/ietf-yang-library:yang-library?content=nonconfig'; /** * Inserts a network element/node. */ public async createNetworkElement(element: NetworkElementConnection): Promise { - const path = this.getNetworkElementConnectDataProviderUri("create"); + const path = this.getNetworkElementConnectDataProviderUri('create'); const result = await requestRest(path, { - method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": element }, replaceUpperCase)) + method: 'POST', body: JSON.stringify(convertPropertyNames({ 'data-provider:input': element }, replaceUpperCase)), }); return result || null; } @@ -49,9 +52,9 @@ class ConnectService { * Updates a network element/node. */ public async updateNetworkElement(element: UpdateNetworkElement): Promise { - const path = this.getNetworkElementConnectDataProviderUri("update"); + const path = this.getNetworkElementConnectDataProviderUri('update'); const result = await requestRest(path, { - method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": element }, replaceUpperCase)) + method: 'POST', body: JSON.stringify(convertPropertyNames({ 'data-provider:input': element }, replaceUpperCase)), }); return result || null; } @@ -61,11 +64,11 @@ class ConnectService { */ public async deleteNetworkElement(element: UpdateNetworkElement): Promise { const query = { - "id": element.id + 'id': element.id, }; - const path = this.getNetworkElementConnectDataProviderUri("delete"); + const path = this.getNetworkElementConnectDataProviderUri('delete'); const result = await requestRest(path, { - method: "POST", body: JSON.stringify(convertPropertyNames({ "data-provider:input": query }, replaceUpperCase)) + method: 'POST', body: JSON.stringify(convertPropertyNames({ 'data-provider:input': query }, replaceUpperCase)), }); return result || null; } @@ -107,13 +110,12 @@ class ConnectService { 'TLS', ' ', '2', - ''].join('') + ''].join(''); let bodyXml; if (networkElement.password) { - bodyXml = mountXml - } - else { - bodyXml = tlsXml + bodyXml = mountXml; + } else { + bodyXml = tlsXml; } try { @@ -121,16 +123,16 @@ class ConnectService { method: 'PUT', headers: { 'Content-Type': 'application/xml', - 'Accept': 'application/xml' + 'Accept': 'application/xml', }, - body: bodyXml + body: bodyXml, }); // expect an empty answer return result !== null; } catch { return false; } - }; + } /** Unmounts a network element by its id. */ public async unmountNetworkElement(nodeId: string): Promise { @@ -141,7 +143,7 @@ class ConnectService { method: 'DELETE', headers: { 'Content-Type': 'application/xml', - 'Accept': 'application/xml' + 'Accept': 'application/xml', }, }); // expect an empty answer @@ -150,15 +152,15 @@ class ConnectService { } catch { return false; } - }; + } /** Yang capabilities of the selected network element/node */ public async infoNetworkElement(nodeId: string): Promise { const path = this.getNetworkElementUri(nodeId); - const topologyRequestPomise = requestRest(path, { method: "GET" }); + const topologyRequestPomise = requestRest(path, { method: 'GET' }); return topologyRequestPomise && topologyRequestPomise.then(result => { - return result && result["network-topology:node"] && result["network-topology:node"][0] || null; + return result && result['network-topology:node'] && result['network-topology:node'][0] || null; }); } @@ -166,13 +168,13 @@ class ConnectService { /** Yang features of the selected network element/node module */ public async infoNetworkElementFeatures(nodeId: string): Promise { const path = this.getNetworkElementYangLibraryFeature(nodeId); - const topologyRequestPomise = requestRest(path, { method: "GET" }); + const topologyRequestPomise = requestRest(path, { method: 'GET' }); return topologyRequestPomise && topologyRequestPomise.then(result => { const resultFinal = result && result['ietf-yang-library:yang-library'] - && result["ietf-yang-library:yang-library"]["module-set"] && - result["ietf-yang-library:yang-library"]["module-set"][0] && - result["ietf-yang-library:yang-library"]["module-set"][0]['module'] || null; + && result['ietf-yang-library:yang-library']['module-set'] && + result['ietf-yang-library:yang-library']['module-set'][0] && + result['ietf-yang-library:yang-library']['module-set'][0].module || null; return resultFinal; }); } @@ -183,22 +185,22 @@ class ConnectService { * Get the connection state of the network element/ node */ public async getNetworkElementConnectionStatus(element: string): Promise<(ConnectionStatus)[] | null> { - const path = `/rests/operations/data-provider:read-network-element-connection-list`; + const path = '/rests/operations/data-provider:read-network-element-connection-list'; const query = { - "data-provider:input": { - "filter": [{ - "property": "node-id", - "filtervalue": element + 'data-provider:input': { + 'filter': [{ + 'property': 'node-id', + 'filtervalue': element, }], - "pagination": { - "size": 20, - "page": 1 - } - } - } - const result = await requestRest>(path, { method: "POST", body: JSON.stringify(query) }); - return result && result["data-provider:output"] && result["data-provider:output"].data && result["data-provider:output"].data.map(ne => ({ - status: ne.status + 'pagination': { + 'size': 20, + 'page': 1, + }, + }, + }; + const result = await requestRest>(path, { method: 'POST', body: JSON.stringify(query) }); + return result && result['data-provider:output'] && result['data-provider:output'].data && result['data-provider:output'].data.map(ne => ({ + status: ne.status, })) || null; } @@ -209,44 +211,43 @@ class ConnectService { public async getTlsKeys(): Promise<(TlsKeys)[] | null> { const path = '/rests/operations/data-provider:read-tls-key-entry'; const query = { - "data-provider:input": { - "filter": [], - "sortorder": [], - "pagination": { - "size": 20, - "page": 1 - } - } + 'data-provider:input': { + 'filter': [], + 'sortorder': [], + 'pagination': { + 'size': 20, + 'page': 1, + }, + }, }; - const result = await requestRest>(path, { method: "POST", body: JSON.stringify(query) }); - return result && result["data-provider:output"] && result["data-provider:output"].data && result["data-provider:output"].data.map(ne => ({ - key: ne + const result = await requestRest>(path, { method: 'POST', body: JSON.stringify(query) }); + return result && result['data-provider:output'] && result['data-provider:output'].data && result['data-provider:output'].data.map(ne => ({ + key: ne, })) || null; } public async getAllWebUriExtensionsForNetworkElementListAsync(neList: string[]): Promise<(guiCutThrough)[]> { - const path = `/rests/operations/data-provider:read-gui-cut-through-entry`; - let webUriList: guiCutThrough[] = [] + const path = '/rests/operations/data-provider:read-gui-cut-through-entry'; + let webUriList: guiCutThrough[] = []; const query = { - "data-provider:input": { - "filter": [{ - "property": "id", - "filtervalues": neList + 'data-provider:input': { + 'filter': [{ + 'property': 'id', + 'filtervalues': neList, }], - "pagination": { - "size": 20, - "page": 1 - } - } - } + 'pagination': { + 'size': 20, + 'page': 1, + }, + }, + }; - const result = await requestRest>(path, { method: "POST", body: JSON.stringify(query) }); - const resultData = result && result["data-provider:output"] && result["data-provider:output"].data; + const result = await requestRest>(path, { method: 'POST', body: JSON.stringify(query) }); + const resultData = result && result['data-provider:output'] && result['data-provider:output'].data; neList.forEach(nodeId => { let entryNotFound = true; if (resultData) { - const BreakException = {}; try { resultData.forEach(entry => { if (entry.id == nodeId) { @@ -256,7 +257,7 @@ class ConnectService { } else { webUriList.push({ id: nodeId, weburi: undefined }); } - throw BreakException; + throw new Error(); } }); } catch (e) { } diff --git a/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx b/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx index 082839718..a6fcb7c32 100644 --- a/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx +++ b/sdnr/wt/odlux/apps/connectApp/src/views/connectView.tsx @@ -15,113 +15,88 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import 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 { AppBar, Tab, Tabs } from '@mui/material'; -import { NetworkElementsList } from '../components/networkElements'; +import { useApplicationDispatch, useSelectApplicationState } from '../../../../framework/src/flux/connect'; + +import { findWebUrisForGuiCutThroughAsyncAction, setPanelAction } from '../actions/commonNetworkElementsActions'; import { ConnectionStatusLog } from '../components/connectionStatusLog'; -import { setPanelAction, findWebUrisForGuiCutThroughAsyncAction, SetWeburiSearchBusy } from '../actions/commonNetworkElementsActions'; +import { NetworkElementsList } from '../components/networkElements'; +import { connectionStatusLogReloadAction } from '../handlers/connectionStatusLogHandler'; +import { networkElementsReloadAction } from '../handlers/networkElementsHandler'; +import { NetworkElementConnection } from '../models/networkElementConnection'; import { PanelId } from '../models/panelId'; -import { NetworkElementConnection } from 'models/networkElementConnection'; -import { AppBar, Tabs, Tab } from '@mui/material'; - -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: (networkElements: NetworkElementConnection[]) => - dispatcher.dispatch(findWebUrisForGuiCutThroughAsyncAction(networkElements.map((ne) => ne.id!))), - isBusy: (busy: boolean) => dispatcher.dispatch(new SetWeburiSearchBusy(busy)), - onLoadConnectionStatusLog: () => { - dispatcher.dispatch(connectionStatusLogReloadAction); - }, - switchActivePanel: (panelId: PanelId) => { - dispatcher.dispatch(setPanelAction(panelId)); - } -}); -type ConnectApplicationComponentProps = Connect; +const ConnectApplicationComponent: React.FC<{}> = () => { -class ConnectApplicationComponent extends React.Component{ + const panelId = useSelectApplicationState(state => state.connect.currentOpenPanel); + const netWorkElements = useSelectApplicationState(state => state.connect.networkElements); - public componentDidMount() { - if (this.props.panelId === null) { //don't change tabs, if one is selected already - this.onTogglePanel("NetworkElements"); - } - //this.props.networkElementsActions.onToggleFilter(); - //this.props.connectionStatusLogActions.onToggleFilter(); - } - - public componentDidUpdate = () => { - - const networkElements = this.props.netWorkElements; - - if (networkElements.rows.length > 0) { - // Update all netWorkElements for propper WebUriClient settings in case of table data changes. - // e.G: Pagination of the table data (there is no event) - this.props.loadWebUris(networkElements.rows); - } - } + const dispatch = useApplicationDispatch(); + const onLoadNetworkElements = () => dispatch(networkElementsReloadAction); + const loadWebUris = (networkElements: NetworkElementConnection[]) => dispatch(findWebUrisForGuiCutThroughAsyncAction(networkElements.map((ne) => ne.id!))); + const onLoadConnectionStatusLog = () => dispatch(connectionStatusLogReloadAction); + const switchActivePanel = (panelId2: PanelId) => dispatch(setPanelAction(panelId2)); - private onTogglePanel = (panelId: PanelId) => { - const nextActivePanel = panelId; - this.props.switchActivePanel(nextActivePanel); + const onTogglePanel = (panelId2: PanelId) => { + const nextActivePanel = panelId2; + switchActivePanel(nextActivePanel); switch (nextActivePanel) { case 'NetworkElements': - this.props.onLoadNetworkElements(); + onLoadNetworkElements(); break; case 'ConnectionStatusLog': - this.props.onLoadConnectionStatusLog(); + onLoadConnectionStatusLog(); break; case null: // do nothing if all panels are closed break; default: - console.warn("Unknown nextActivePanel [" + nextActivePanel + "] in connectView"); + console.warn('Unknown nextActivePanel [' + nextActivePanel + '] in connectView'); break; } - }; - private onHandleTabChange = (event: React.SyntheticEvent, newValue: PanelId) => { - this.props.switchActivePanel(newValue); - } - - render(): JSX.Element { - const { panelId: activePanelId } = this.props; - - return ( - <> - - - - - - - {activePanelId === 'NetworkElements' - ? - : activePanelId === 'ConnectionStatusLog' - ? - : null} - - ); + const onHandleTabChange = (event: React.SyntheticEvent, newValue: PanelId) => { + switchActivePanel(newValue); }; + React.useEffect(()=>{ + if (panelId === null) { //don't change tabs, if one is selected already + onTogglePanel('NetworkElements'); + } + }, []); -} + React.useEffect(()=>{ + const networkElements = netWorkElements; -export const ConnectApplication = (connect(mapProps, mapDispatcher)(ConnectApplicationComponent)); + if (networkElements.rows.length > 0) { + // Search for weburi client for all netWorkElements in case of table data changes. + // e.G: Pagination of the table data (there is no event) + loadWebUris(networkElements.rows); + } + }, [netWorkElements]); + + return ( + <> + + + + + + + {panelId === 'NetworkElements' + ? + : panelId === 'ConnectionStatusLog' + ? + : null + } + + ); +}; + +export const ConnectApplication = ConnectApplicationComponent; export default ConnectApplication; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/connectApp/tsconfig.json b/sdnr/wt/odlux/apps/connectApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/connectApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/connectApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/connectApp/webpack.config.js b/sdnr/wt/odlux/apps/connectApp/webpack.config.js index ff76904c5..b7aebb9df 100644 --- a/sdnr/wt/odlux/apps/connectApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/connectApp/webpack.config.js @@ -59,6 +59,16 @@ module.exports = (env) => { use: [{ loader: "babel-loader" }] + },{ + //don't minify images + test: /\.(png|gif|jpg|svg)$/, + use: [{ + loader: 'url-loader', + options: { + limit: 10, + name: './images/[name].[ext]' + } + }] }] }, diff --git a/sdnr/wt/odlux/apps/demoApp/package.json b/sdnr/wt/odlux/apps/demoApp/package.json index 951332a28..6a31bc3ec 100644 --- a/sdnr/wt/odlux/apps/demoApp/package.json +++ b/sdnr/wt/odlux/apps/demoApp/package.json @@ -21,6 +21,9 @@ "author": "Matthias Fischer", "license": "Apache-2.0", "dependencies": { + "@fortawesome/fontawesome-svg-core": "1.2.35", + "@fortawesome/free-solid-svg-icons": "5.6.3", + "@fortawesome/react-fontawesome": "0.1.14", "@emotion/react": "^11.7.0", "@emotion/styled": "^11.6.0", "@mui/icons-material": "^5.2.0", diff --git a/sdnr/wt/odlux/apps/demoApp/pom.xml b/sdnr/wt/odlux/apps/demoApp/pom.xml index 07f990b79..172a43561 100644 --- a/sdnr/wt/odlux/apps/demoApp/pom.xml +++ b/sdnr/wt/odlux/apps/demoApp/pom.xml @@ -19,6 +19,7 @@ ~ ============LICENSE_END======================================================= ~ --> + 4.0.0 @@ -139,7 +140,7 @@ initialize - v12.13.0 + v12.22.0 v1.22.10 diff --git a/sdnr/wt/odlux/apps/demoApp/src/actions/authorActions.ts b/sdnr/wt/odlux/apps/demoApp/src/actions/authorActions.ts index f75075192..f22d1e0a3 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/actions/authorActions.ts +++ b/sdnr/wt/odlux/apps/demoApp/src/actions/authorActions.ts @@ -26,16 +26,13 @@ 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(); - } } @@ -47,5 +44,5 @@ export const loadAllAuthorsAsync = (dispatch: Dispatch) => { 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 index 6b960cdae..1aad97451 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/components/counter.tsx +++ b/sdnr/wt/odlux/apps/demoApp/src/components/counter.tsx @@ -15,20 +15,15 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React, { FC, useState } from 'react'; -export class Counter extends React.Component<{}, { counter: number }> { - constructor(props: {}) { - super(props); +const Counter: FC = () => { + const [counter, setCounter] = useState(0); + return ( + + ); +}; - this.state = { - counter: 0 - }; - } - - render() { - return ( - - ) - } -} \ No newline at end of file +Counter.displayName = 'Counter'; + +export { Counter }; \ 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 index 9ff8450c8..1f920f2a8 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/handlers/demoAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/demoApp/src/handlers/demoAppRootHandler.ts @@ -18,6 +18,7 @@ import { combineActionHandler } from '../../../../framework/src/flux/middleware'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; import { listAuthorsHandler, IListAuthors } from './listAuthorsHandler'; @@ -30,7 +31,7 @@ export interface IDemoAppStoreState { declare module '../../../../framework/src/store/applicationStore' { interface IApplicationStoreState { - demo: IDemoAppStoreState + demo: IDemoAppStoreState; } } diff --git a/sdnr/wt/odlux/apps/demoApp/src/handlers/editAuthorHandler.ts b/sdnr/wt/odlux/apps/demoApp/src/handlers/editAuthorHandler.ts index 34b533cb1..1d37a36cc 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/handlers/editAuthorHandler.ts +++ b/sdnr/wt/odlux/apps/demoApp/src/handlers/editAuthorHandler.ts @@ -25,9 +25,9 @@ export interface IEditAuthor { const editAuthorInit: IEditAuthor = { author: null, - isDirty: false + isDirty: false, }; -export const editAuthorHandler: IActionHandler = (state = editAuthorInit, action) => { +export const editAuthorHandler: IActionHandler = (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 index ca2b6d3c6..c85eaff04 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/handlers/listAuthorsHandler.ts +++ b/sdnr/wt/odlux/apps/demoApp/src/handlers/listAuthorsHandler.ts @@ -27,7 +27,7 @@ export interface IListAuthors { const listAuthorsInit: IListAuthors = { authors: [], - busy: false + busy: false, }; export const listAuthorsHandler: IActionHandler = (state = listAuthorsInit, action) => { @@ -35,7 +35,7 @@ export const listAuthorsHandler: IActionHandler = (state = listAut state = { ...state, - busy: true + busy: true, }; } else if (action instanceof AllAuthorsLoadedAction) { @@ -43,12 +43,12 @@ export const listAuthorsHandler: IActionHandler = (state = listAut state = { ...state, authors: action.authors, - busy: false + busy: false, }; } else { state = { ...state, - busy: false + busy: false, }; } } diff --git a/sdnr/wt/odlux/apps/demoApp/src/models/author.ts b/sdnr/wt/odlux/apps/demoApp/src/models/author.ts index 0aaa308a2..bdd414cba 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/models/author.ts +++ b/sdnr/wt/odlux/apps/demoApp/src/models/author.ts @@ -21,7 +21,7 @@ */ export interface IAuthor { /** - * Defines the unique id of the autor. + * Defines the unique id of the author. */ id: number; diff --git a/sdnr/wt/odlux/apps/demoApp/src/plugin.tsx b/sdnr/wt/odlux/apps/demoApp/src/plugin.tsx index 4d67c28ac..7b29b4045 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/plugin.tsx +++ b/sdnr/wt/odlux/apps/demoApp/src/plugin.tsx @@ -15,13 +15,13 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from "react"; +import React from 'react'; import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; -import { faAddressBook, faRegistered } from '@fortawesome/free-solid-svg-icons'; +import { faAddressBook } from '@fortawesome/free-solid-svg-icons/faAddressBook'; import applicationManager from '../../../framework/src/services/applicationManager'; -import connect, { Connect } from '../../../framework/src/flux/connect'; +import { connect, Connect } from '../../../framework/src/flux/connect'; import { demoAppRootHandler } from './handlers/demoAppRootHandler'; @@ -43,12 +43,12 @@ const App = (props: AppProps) => ( const FinalApp = withRouter(connect()(App)); export function register() { - const applicationApi = applicationManager.registerApplication({ - name: "demo", + applicationManager.registerApplication({ + name: 'demo', icon: faAddressBook, rootComponent: FinalApp, rootActionHandler: demoAppRootHandler, exportedComponents: { counter: Counter }, - menuEntry: "Demo" + menuEntry: 'Demo', }); } diff --git a/sdnr/wt/odlux/apps/demoApp/src/services/authorService.ts b/sdnr/wt/odlux/apps/demoApp/src/services/authorService.ts index 13e4b316c..deaa2ff76 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/services/authorService.ts +++ b/sdnr/wt/odlux/apps/demoApp/src/services/authorService.ts @@ -26,39 +26,39 @@ const base_url = 'https://api.mfico.de/v1/authors'; */ 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 { return new Promise((resolve: (value: IAuthor[]) => void, reject: (err: any) => void) => { - $.ajax({ method: "GET", url: base_url }) - .then((data) => { resolve(data); }, (err) => { reject(err) }); + $.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 { 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) }); + $.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 { return new Promise((resolve: (value: IAuthor) => void, reject: (err: any) => void) => { - // simulate server save + // simulate server save window.setTimeout(() => { if (Math.random() > 0.8) { - reject("Could not save author."); + reject('Could not save author.'); } else { resolve(author); } diff --git a/sdnr/wt/odlux/apps/demoApp/src/views/authorsList.tsx b/sdnr/wt/odlux/apps/demoApp/src/views/authorsList.tsx index b56058d36..5d9f13a55 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/views/authorsList.tsx +++ b/sdnr/wt/odlux/apps/demoApp/src/views/authorsList.tsx @@ -15,7 +15,7 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; import Table from '@mui/material/Table'; @@ -25,22 +25,28 @@ import TableHead from '@mui/material/TableHead'; import TableRow from '@mui/material/TableRow'; import Paper from '@mui/material/Paper'; // means border -import connect from '../../../../framework/src/flux/connect'; +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 + authors: IAuthor[]; + busy: boolean; + onLoadAllAuthors: () => void; } class AuthorsListComponent extends React.Component { render(): JSX.Element { const { authors, busy } = this.props; - return ( + return busy + ? ( + + Loading + + ) + : ( @@ -52,7 +58,7 @@ class AuthorsListComponent extends React.Component {authors.map(author => ( - this.editAuthor(author)}> + this.editAuthor(author)}> {author.id} {author.firstName} {author.lastName} @@ -61,15 +67,15 @@ class AuthorsListComponent extends React.Component
    - ); - }; + ); + } public componentDidMount() { this.props.onLoadAllAuthors(); } private editAuthor = (author: IAuthor) => { - author && this.props.history.push(this.props.match.path + '/' + author.id); + if (author) this.props.history.push(this.props.match.path + '/' + author.id); }; } @@ -77,11 +83,11 @@ export const AuthorsList = withRouter( connect( ({ demo: state }) => ({ authors: state.listAuthors.authors, - busy: state.listAuthors.busy + busy: state.listAuthors.busy, }), (dispatcher) => ({ onLoadAllAuthors: () => { - dispatcher.dispatch(loadAllAuthorsAsync) - } + 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 index 92f671234..0da619ba2 100644 --- a/sdnr/wt/odlux/apps/demoApp/src/views/editAuthor.tsx +++ b/sdnr/wt/odlux/apps/demoApp/src/views/editAuthor.tsx @@ -15,10 +15,10 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; -type EditAuthorProps = RouteComponentProps<{ authorId: string}>; +type EditAuthorProps = RouteComponentProps<{ authorId: string }>; class EditAuthorComponent extends React.Component { render(): JSX.Element { @@ -26,7 +26,7 @@ class EditAuthorComponent extends React.Component {

    Edit Author { this.props.match.params.authorId }

    - ) + ); } } diff --git a/sdnr/wt/odlux/apps/demoApp/tsconfig.json b/sdnr/wt/odlux/apps/demoApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/demoApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/demoApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/eventLogApp/pom.xml b/sdnr/wt/odlux/apps/eventLogApp/pom.xml index 19d9ad0c1..9e4ed5fe7 100644 --- a/sdnr/wt/odlux/apps/eventLogApp/pom.xml +++ b/sdnr/wt/odlux/apps/eventLogApp/pom.xml @@ -19,6 +19,7 @@ ~ ============LICENSE_END======================================================= ~ --> + 4.0.0 @@ -139,7 +140,7 @@ initialize - v12.13.0 + v12.22.0 v1.22.10 diff --git a/sdnr/wt/odlux/apps/eventLogApp/src/assets/icons/eventLogAppIcon.svg b/sdnr/wt/odlux/apps/eventLogApp/src/assets/icons/eventLogAppIcon.svg new file mode 100644 index 000000000..743167d2c --- /dev/null +++ b/sdnr/wt/odlux/apps/eventLogApp/src/assets/icons/eventLogAppIcon.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + diff --git a/sdnr/wt/odlux/apps/eventLogApp/src/pluginEventLog.tsx b/sdnr/wt/odlux/apps/eventLogApp/src/pluginEventLog.tsx index a2edb436f..8ee322a8e 100644 --- a/sdnr/wt/odlux/apps/eventLogApp/src/pluginEventLog.tsx +++ b/sdnr/wt/odlux/apps/eventLogApp/src/pluginEventLog.tsx @@ -17,24 +17,26 @@ */ // 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 React, { FC } from 'react'; + import applicationManager from '../../../framework/src/services/applicationManager'; import { EventLog } from './views/eventLog'; import eventLogAppRootHandler from './handlers/eventLogAppRootHandler'; -const App : React.SFC = (props) => { - return +const appIcon = require('./assets/icons/eventLogAppIcon.svg'); // select app icon + +const App : FC = () => { + return ; }; export function register() { applicationManager.registerApplication({ - name: "eventLog", - icon: faBookOpen, + name: 'eventLog', + icon: appIcon, rootActionHandler: eventLogAppRootHandler, rootComponent: App, - menuEntry: "EventLog" + menuEntry: 'EventLog', }); } diff --git a/sdnr/wt/odlux/apps/eventLogApp/tsconfig.json b/sdnr/wt/odlux/apps/eventLogApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/eventLogApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/eventLogApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/eventLogApp/webpack.config.js b/sdnr/wt/odlux/apps/eventLogApp/webpack.config.js index de309c1ba..3d056ece1 100644 --- a/sdnr/wt/odlux/apps/eventLogApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/eventLogApp/webpack.config.js @@ -57,6 +57,16 @@ module.exports = (env) => { use: [{ loader: "babel-loader" }] + }, { + //don't minify images + test: /\.(png|gif|jpg|svg)$/, + use: [{ + loader: 'url-loader', + options: { + limit: 10, + name: './images/[name].[ext]' + } + }] }] }, diff --git a/sdnr/wt/odlux/apps/faultApp/package.json b/sdnr/wt/odlux/apps/faultApp/package.json index a5958d8c4..953105584 100644 --- a/sdnr/wt/odlux/apps/faultApp/package.json +++ b/sdnr/wt/odlux/apps/faultApp/package.json @@ -26,7 +26,9 @@ "@mui/icons-material": "^5.2.0", "@mui/material": "^5.2.2", "@mui/styles": "^5.2.2", - "@odlux/framework": "*" + "@odlux/framework": "*", + "@fortawesome/free-solid-svg-icons": "5.6.3", + "@fortawesome/react-fontawesome": "0.1.14" }, "peerDependencies": { "@types/classnames": "2.2.6", @@ -38,6 +40,7 @@ "jquery": "3.3.1", "react": "17.0.2", "react-dom": "17.0.2", - "react-router-dom": "5.2.0" + "react-router-dom": "5.2.0", + "react-chartjs-2": "^3.0.3" } } diff --git a/sdnr/wt/odlux/apps/faultApp/pom.xml b/sdnr/wt/odlux/apps/faultApp/pom.xml index 31a376014..ba4df509b 100644 --- a/sdnr/wt/odlux/apps/faultApp/pom.xml +++ b/sdnr/wt/odlux/apps/faultApp/pom.xml @@ -19,6 +19,7 @@ ~ ============LICENSE_END======================================================= ~ --> + 4.0.0 @@ -139,7 +140,7 @@ initialize - v12.13.0 + v12.22.0 v1.22.10 diff --git a/sdnr/wt/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.ts b/sdnr/wt/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.ts index cdcbf64d2..7aac8ba35 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/actions/clearStuckAlarmsAction.ts @@ -17,20 +17,21 @@ */ -import { clearStuckAlarms } from "../services/faultStatusService" -import { Dispatch } from "../../../../framework/src/flux/store"; -import { FaultApplicationBaseAction } from "./notificationActions"; +import { Dispatch } from '../../../../framework/src/flux/store'; + +import { clearStuckAlarms } from '../services/faultStatusService'; +import { FaultApplicationBaseAction } from './notificationActions'; export class AreStuckAlarmsCleared extends FaultApplicationBaseAction { - constructor(public isBusy: boolean) { - super(); - } + constructor(public isBusy: boolean) { + super(); + } } export const clearStuckAlarmAsyncAction = (dispatch: Dispatch) => async (nodeNames: string[]) => { - dispatch(new AreStuckAlarmsCleared(true)); - const result = await clearStuckAlarms(nodeNames).catch(error => { console.error(error); return undefined }); - dispatch(new AreStuckAlarmsCleared(false)); - return result; -} \ No newline at end of file + dispatch(new AreStuckAlarmsCleared(true)); + const result = await clearStuckAlarms(nodeNames).catch(error => { console.error(error); return undefined; }); + dispatch(new AreStuckAlarmsCleared(false)); + return result; +}; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src/actions/panelChangeActions.ts b/sdnr/wt/odlux/apps/faultApp/src/actions/panelChangeActions.ts index 7cf02ac4f..fb29e9c1b 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/actions/panelChangeActions.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/actions/panelChangeActions.ts @@ -16,6 +16,7 @@ * ============LICENSE_END========================================================================== */ import { Action } from '../../../../framework/src/flux/action'; + import { PanelId } from '../models/panelId'; export class SetPanelAction extends Action { @@ -32,5 +33,5 @@ export class RememberCurrentPanelAction extends Action { export const setPanelAction = (panelId: PanelId) => { return new SetPanelAction(panelId); -} +}; diff --git a/sdnr/wt/odlux/apps/faultApp/src/actions/statusActions.ts b/sdnr/wt/odlux/apps/faultApp/src/actions/statusActions.ts index 54fea6a5f..8b631b96d 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/actions/statusActions.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/actions/statusActions.ts @@ -15,10 +15,11 @@ * the License. * ============LICENSE_END========================================================================== */ -import { FaultApplicationBaseAction } from './notificationActions'; -import { getFaultStateFromDatabase } from '../services/faultStatusService'; import { Dispatch } from '../../../../framework/src/flux/store'; +import { getFaultStateFromDatabase } from '../services/faultStatusService'; +import { FaultApplicationBaseAction } from './notificationActions'; + export class SetFaultStatusAction extends FaultApplicationBaseAction { constructor(public criticalFaults: number, public majorFaults: number, public minorFaults: number, public warnings: number, @@ -32,29 +33,28 @@ export class SetFaultStatusAction extends FaultApplicationBaseAction { export const refreshFaultStatusAsyncAction = async (dispatch: Dispatch) => { - dispatch(new SetFaultStatusAction(0, 0, 0, 0, true, 0, 0, 0, 0, 0, 0, 0, 0, true)); + // dispatch(new SetFaultStatusAction(0, 0, 0, 0, true, 0, 0, 0, 0, 0, 0, 0, 0, true)); const result = await getFaultStateFromDatabase().catch(_ => null); if (result) { const statusAction = new SetFaultStatusAction( - result["Critical"] || 0, - result["Major"] || 0, - result["Minor"] || 0, - result["Warning"] || 0, + result.Critical || 0, + result.Major || 0, + result.Minor || 0, + result.Warning || 0, + false, + result.Connected || 0, + result.Connecting || 0, + result.Disconnected || 0, + result.Mounted || 0, + result.UnableToConnect || 0, + result.Undefined || 0, + result.Unmounted || 0, + result.total || 0, false, - result["Connected"] || 0, - result["Connecting"] || 0, - result["Disconnected"] || 0, - result["Mounted"] || 0, - result["UnableToConnect"] || 0, - result["Undefined"] || 0, - result["Unmounted"] || 0, - result["total"] || 0, - false ); dispatch(statusAction); return; - } - else { + } else { dispatch(new SetFaultStatusAction(0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, false)); } -} +}; diff --git a/sdnr/wt/odlux/apps/faultApp/src/assets/icons/faultAppIcon.svg b/sdnr/wt/odlux/apps/faultApp/src/assets/icons/faultAppIcon.svg new file mode 100644 index 000000000..aabbf4cf4 --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/assets/icons/faultAppIcon.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx index 463c2079c..e86b756a7 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/components/clearStuckAlarmsDialog.tsx @@ -16,119 +16,122 @@ * ============LICENSE_END========================================================================== */ -import * as React from 'react' -import { DialogContent, DialogActions, Button, Dialog, DialogTitle, DialogContentText } from '@mui/material'; -import { currentProblemsReloadAction } from '../handlers/currentProblemsHandler'; +import React from 'react'; + +import { Button, Dialog, DialogActions, DialogContent, DialogContentText, DialogTitle } from '@mui/material'; + +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + import { clearStuckAlarmAsyncAction } from '../actions/clearStuckAlarmsAction'; -import connect, { IDispatcher, Connect } from '../../../../framework/src/flux/connect'; +import { currentAlarmsReloadAction } from '../handlers/currentAlarmsHandler'; export enum ClearStuckAlarmsDialogMode { - None = "none", - Show = "show" + None = 'none', + Show = 'show', } const mapDispatch = (dispatcher: IDispatcher) => ({ - clearStuckAlarmsAsync: clearStuckAlarmAsyncAction(dispatcher.dispatch), - reloadCurrentProblemsAction: () => dispatcher.dispatch(currentProblemsReloadAction) + clearStuckAlarmsAsync: clearStuckAlarmAsyncAction(dispatcher.dispatch), + reloadCurrentAlarmsAction: () => dispatcher.dispatch(currentAlarmsReloadAction), }); type clearStuckAlarmsProps = Connect & { - numberDevices: Number; - mode: ClearStuckAlarmsDialogMode; - stuckAlarms: string[]; - onClose: () => void; -} + numberDevices: Number; + mode: ClearStuckAlarmsDialogMode; + stuckAlarms: string[]; + onClose: () => void; +}; type ClearStuckAlarmsState = { - clearAlarmsSuccessful: boolean; - errormessage: string; - unclearedAlarms: string[]; -} - -class ClearStuckAlarmsDialogComponent extends React.Component{ - constructor(props: clearStuckAlarmsProps) { - super(props); - this.state = { - clearAlarmsSuccessful: true, - errormessage: '', - unclearedAlarms: [] - }; - } - - onClose = (event: React.MouseEvent) => { - event.stopPropagation(); - event.preventDefault(); - this.props.onClose(); + clearAlarmsSuccessful: boolean; + errormessage: string; + unclearedAlarms: string[]; +}; + +class ClearStuckAlarmsDialogComponent extends React.Component { + 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 && result['devicemanager:output'].nodenames && result['devicemanager:output'].nodenames.length !== this.props.stuckAlarms.length) { //show errormessage if not all devices were cleared + const undeletedAlarm = this.props.stuckAlarms.filter(item => !result['devicemanager: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: [] }); } - onRefresh = async (event: React.MouseEvent) => { - event.stopPropagation(); - event.preventDefault(); - const result = await this.props.clearStuckAlarmsAsync(this.props.stuckAlarms); - - if (result && result["devicemanager:output"].nodenames && result["devicemanager:output"].nodenames.length !== this.props.stuckAlarms.length) { //show errormessage if not all devices were cleared - const undeletedAlarm = this.props.stuckAlarms.filter(item => !result["devicemanager: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: [] }); - } - - 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 ( - - {title} - - - {message} - - { - this.state.unclearedAlarms.map(item => - - {item} - - ) - } - - - { - this.state.clearAlarmsSuccessful && - <> - - - - } - - { - !this.state.clearAlarmsSuccessful && - } - - - ) - } + this.props.reloadCurrentAlarmsAction(); + this.props.onClose(); + }; + + onOk = (event: React.MouseEvent) => { + + event.stopPropagation(); + event.preventDefault(); + if (this.state.unclearedAlarms.length > 0) + this.props.reloadCurrentAlarmsAction(); + 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 ( + + {title} + + + {message} + + { + this.state.unclearedAlarms.map(item => + + {item} + , + ) + } + + + { + this.state.clearAlarmsSuccessful && + <> + + + + } + + { + !this.state.clearAlarmsSuccessful && + } + + + ); + } } const ClearStuckAlarmsDialog = connect(undefined, mapDispatch)(ClearStuckAlarmsDialogComponent); diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/dashboardHome.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/dashboardHome.tsx index a81705965..a3e32c42c 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/components/dashboardHome.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/components/dashboardHome.tsx @@ -15,26 +15,25 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; -import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect';; -import { Theme } from '@mui/material'; import { WithStyles } from '@mui/styles'; import createStyles from '@mui/styles/createStyles'; -import withStyles from '@mui/styles/withStyles'; import { Doughnut } from 'react-chartjs-2'; -import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -const styles = (theme: Theme) => createStyles({ +const styles = () => createStyles({ pageWidthSettings: { width: '50%', - float: 'left' + float: 'left', }, -}) +}); -const scrollbar = { overflow: "auto", paddingRight: "20px" } +const scrollbar = { overflow: 'auto', paddingRight: '20px' }; let connectionStatusinitialLoad = true; let connectionStatusinitialStateChanged = false; @@ -47,7 +46,7 @@ let alarmStatusDataLoad: number[] = [0, 0, 0, 0]; let alarmTotalCount = 0; const mapProps = (state: IApplicationStoreState) => ({ - alarmStatus: state.fault.faultStatus + alarmStatus: state.fault.faultStatus, }); const mapDispatch = (dispatcher: IDispatcher) => ({ @@ -60,11 +59,10 @@ class DashboardHome extends React.Component { constructor(props: HomeComponentProps) { super(props); this.state = { - } + }; } render(): JSX.Element { - const { classes } = this.props; if (!this.props.alarmStatus.isLoadingConnectionStatusChart) { connectionStatusDataLoad = [ @@ -72,7 +70,7 @@ class DashboardHome extends React.Component { this.props.alarmStatus.Connecting, this.props.alarmStatus.Disconnected, this.props.alarmStatus.UnableToConnect, - this.props.alarmStatus.Undefined + this.props.alarmStatus.Undefined, ]; connectionTotalCount = this.props.alarmStatus.Connected + this.props.alarmStatus.Connecting + this.props.alarmStatus.Disconnected + this.props.alarmStatus.UnableToConnect + this.props.alarmStatus.Undefined; @@ -84,7 +82,7 @@ class DashboardHome extends React.Component { this.props.alarmStatus.critical, this.props.alarmStatus.major, this.props.alarmStatus.minor, - this.props.alarmStatus.warning + this.props.alarmStatus.warning, ]; alarmTotalCount = this.props.alarmStatus.critical + this.props.alarmStatus.major + this.props.alarmStatus.minor + this.props.alarmStatus.warning; @@ -97,7 +95,7 @@ class DashboardHome extends React.Component { 'Connecting: ' + this.props.alarmStatus.Connecting, 'Disconnected: ' + this.props.alarmStatus.Disconnected, 'UnableToConnect: ' + this.props.alarmStatus.UnableToConnect, - 'Undefined: ' + this.props.alarmStatus.Undefined + 'Undefined: ' + this.props.alarmStatus.Undefined, ], datasets: [{ labels: ['Connected', 'Connecting', 'Disconnected', 'UnableToConnect', 'Undefined'], @@ -107,9 +105,9 @@ class DashboardHome extends React.Component { 'rgb(255, 102, 0)', 'rgb(191, 191, 191)', 'rgb(191, 191, 191)', - 'rgb(242, 240, 240)' - ] - }] + 'rgb(242, 240, 240)', + ], + }], }; @@ -119,9 +117,9 @@ class DashboardHome extends React.Component { datasets: [{ data: [1], backgroundColor: [ - 'rgb(255, 255, 255)' - ] - }] + 'rgb(255, 255, 255)', + ], + }], }; /** Loading Connection Status chart */ @@ -130,9 +128,9 @@ class DashboardHome extends React.Component { datasets: [{ data: [1], backgroundColor: [ - 'rgb(255, 255, 255)' - ] - }] + 'rgb(255, 255, 255)', + ], + }], }; /** Loading Alarm Status chart */ @@ -141,9 +139,9 @@ class DashboardHome extends React.Component { datasets: [{ data: [1], backgroundColor: [ - 'rgb(255, 255, 255)' - ] - }] + 'rgb(255, 255, 255)', + ], + }], }; /** Connection status options */ @@ -155,57 +153,57 @@ class DashboardHome extends React.Component { let label = (data.datasets[tooltipItem.datasetIndex].labels && data.datasets[tooltipItem.datasetIndex].labels[ - tooltipItem.index + tooltipItem.index ]) || data.labels[tooltipItem.index] || - ""; + ''; if (label) { - label += ": "; + label += ': '; } label += data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] + - (data.datasets[tooltipItem.datasetIndex].labelSuffix || ""); + (data.datasets[tooltipItem.datasetIndex].labelSuffix || ''); return label; - } - } + }, + }, }, responsive: true, maintainAspectRatio: false, animation: { - duration: 0 + duration: 0, }, plugins: { legend: { display: true, - position: 'top' - } + position: 'top', + }, }, onClick: (event: MouseEvent, item: any) => { if (item[0]) { let connectionStatus = labels[item[0]._index] + ''; - this.props.navigateToApplication("connect", '/connectionStatus/' + connectionStatus); + this.props.navigateToApplication('connect', '/connectionStatus/' + connectionStatus); } - } - } + }, + }; /** Connection status unavailable options */ const connectionStatusUnavailableOptions = { responsive: true, maintainAspectRatio: false, animation: { - duration: 0 + duration: 0, }, plugins: { legend: { display: true, - position: 'top' + position: 'top', }, tooltip: { - enabled: false - } - } - } + enabled: false, + }, + }, + }; /** Add text inside the doughnut chart for Connection Status */ const connectionStatusPlugins = [{ @@ -215,15 +213,15 @@ class DashboardHome extends React.Component { ctx = chart.ctx; ctx.restore(); var fontSize = (height / 480).toFixed(2); - ctx.font = fontSize + "em sans-serif"; - ctx.textBaseline = "top"; - var text = "Network Connection Status", + ctx.font = fontSize + 'em sans-serif'; + ctx.textBaseline = 'top'; + var text = 'Network Connection Status', textX = Math.round((width - ctx.measureText(text).width) / 2), textY = height / 2; ctx.fillText(text, textX, textY); ctx.save(); - } - }] + }, + }]; /** Alarm status Data */ const alarmStatusData = { @@ -231,7 +229,7 @@ class DashboardHome extends React.Component { 'Critical : ' + this.props.alarmStatus.critical, 'Major : ' + this.props.alarmStatus.major, 'Minor : ' + this.props.alarmStatus.minor, - 'Warning : ' + this.props.alarmStatus.warning + 'Warning : ' + this.props.alarmStatus.warning, ], datasets: [{ labels: ['Critical', 'Major', 'Minor', 'Warning'], @@ -240,10 +238,10 @@ class DashboardHome extends React.Component { 'rgb(240, 25, 10)', 'rgb(240, 133, 10)', 'rgb(240, 240, 10)', - 'rgb(46, 115, 176)' + 'rgb(46, 115, 176)', ], - }] - } + }], + }; /** No Alarm status available */ const alarmStatusUnavailableData = { @@ -251,9 +249,9 @@ class DashboardHome extends React.Component { datasets: [{ data: [1], backgroundColor: [ - 'rgb(0, 153, 51)' - ] - }] + 'rgb(0, 153, 51)', + ], + }], }; /** Alarm status Options */ @@ -265,36 +263,36 @@ class DashboardHome extends React.Component { let label = (data.datasets[tooltipItem.datasetIndex].labels && data.datasets[tooltipItem.datasetIndex].labels[ - tooltipItem.index + tooltipItem.index ]) || data.labels[tooltipItem.index] || - ""; + ''; if (label) { - label += ": "; + label += ': '; } label += data.datasets[tooltipItem.datasetIndex].data[tooltipItem.index] + - (data.datasets[tooltipItem.datasetIndex].labelSuffix || ""); + (data.datasets[tooltipItem.datasetIndex].labelSuffix || ''); return label; - } - } + }, + }, }, responsive: true, maintainAspectRatio: false, animation: { - duration: 0 + duration: 0, }, plugins: { legend: { display: true, - position: 'top' - } + position: 'top', + }, }, onClick: (event: MouseEvent, item: any) => { if (item[0]) { let severity = alarmLabels[item[0]._index] + ''; - this.props.navigateToApplication("fault", '/alarmStatus/' + severity); + this.props.navigateToApplication('fault', '/alarmStatus/' + severity); } }, }; @@ -304,18 +302,18 @@ class DashboardHome extends React.Component { responsive: true, maintainAspectRatio: false, animation: { - duration: 0 + duration: 0, }, plugins: { legend: { display: true, - position: 'top' + position: 'top', }, tooltip: { - enabled: false - } - } - } + enabled: false, + }, + }, + }; /** Add text inside the doughnut chart for Alarm Status */ const alarmStatusPlugins = [{ beforeDraw: function (chart: any) { @@ -324,15 +322,15 @@ class DashboardHome extends React.Component { ctx = chart.ctx; ctx.restore(); var fontSize = (height / 480).toFixed(2); - ctx.font = fontSize + "em sans-serif"; - ctx.textBaseline = "top"; - var text = "Network Alarm Status", + ctx.font = fontSize + 'em sans-serif'; + ctx.textBaseline = 'top'; + var text = 'Network Alarm Status', textX = Math.round((width - ctx.measureText(text).width) / 2), textY = height / 2; ctx.fillText(text, textX, textY); ctx.save(); - } - }] + }, + }]; return ( <> @@ -397,7 +395,7 @@ class DashboardHome extends React.Component {
    - ) + ); } /** Check if connection status data available */ @@ -412,7 +410,7 @@ class DashboardHome extends React.Component { } else { return true; } - } + }; /** Check if connection status chart data is loaded */ public checkElementsAreLoaded = () => { @@ -434,7 +432,7 @@ class DashboardHome extends React.Component { return !isLoadingCheck.isLoadingConnectionStatusChart; } return true; - } + }; /** Check if alarms data available */ public checkAlarmStatus = () => { @@ -444,11 +442,10 @@ class DashboardHome extends React.Component { } if (alarmCount.critical == 0 && alarmCount.major == 0 && alarmCount.minor == 0 && alarmCount.warning == 0) { return false; - } - else { + } else { return true; } - } + }; /** Check if alarm status chart data is loaded */ public checkAlarmsAreLoaded = () => { @@ -470,7 +467,7 @@ class DashboardHome extends React.Component { return !isLoadingCheck.isLoadingAlarmStatusChart; } return true; - } + }; } export default (withRouter(connect(mapProps, mapDispatch)(DashboardHome))); \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/faultStatus.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/faultStatus.tsx index 7820dfdeb..c2ca4a536 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/components/faultStatus.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/components/faultStatus.tsx @@ -15,37 +15,36 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; -import { Theme } from '@mui/material/styles'; +import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; // select app icon +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import Typography from '@mui/material/Typography'; import { WithStyles } from '@mui/styles'; -import withStyles from '@mui/styles/withStyles'; import createStyles from '@mui/styles/createStyles'; -import { faExclamationTriangle } from '@fortawesome/free-solid-svg-icons'; // select app icon +import withStyles from '@mui/styles/withStyles'; -import connect, { Connect } from '../../../../framework/src/flux/connect'; +import { connect, Connect } from '../../../../framework/src/flux/connect'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import Typography from '@mui/material/Typography'; -import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -const styles = (theme: Theme) => createStyles({ +const styles = () => createStyles({ icon: { marginLeft: 8, - marginRight: 8 + marginRight: 8, }, critical: { - color: "red" + color: 'red', }, major: { - color: "orange" + color: 'orange', }, minor: { - color: "#f7f700" + color: '#f7f700', }, warning: { - color: "#428bca" - } + color: '#428bca', + }, }); const mapProps = (state: IApplicationStoreState) => ({ @@ -75,7 +74,7 @@ class FaultStatusComponent extends React.Component { ); - }; + } } export const FaultStatus = withStyles(styles)(connect(mapProps)(FaultStatusComponent)); diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/refreshAlarmLogDialog.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/refreshAlarmLogDialog.tsx index 8c639eec9..59657d8de 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/components/refreshAlarmLogDialog.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/components/refreshAlarmLogDialog.tsx @@ -24,78 +24,73 @@ import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { alarmLogEntriesReloadAction } from '../handlers/alarmLogEntriesHandler'; -import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; - import { Fault } from '../models/fault'; export enum RefreshAlarmLogDialogMode { - None = "none", - RefreshAlarmLogTable = "RefreshAlarmLogTable", + None = 'none', + RefreshAlarmLogTable = 'RefreshAlarmLogTable', } const mapDispatch = (dispatcher: IDispatcher) => ({ - refreshAlarmLog: () => dispatcher.dispatch(alarmLogEntriesReloadAction) + refreshAlarmLog: () => dispatcher.dispatch(alarmLogEntriesReloadAction), }); type DialogSettings = { - dialogTitle: string, - dialogDescription: string, - applyButtonText: string, - cancelButtonText: string, - enableMountIdEditor: boolean, - enableUsernameEditor: boolean, - enableExtendedEditor: boolean, -} + dialogTitle: string; + dialogDescription: string; + applyButtonText: string; + cancelButtonText: string; + enableMountIdEditor: boolean; + enableUsernameEditor: boolean; + enableExtendedEditor: boolean; +}; const settings: { [key: string]: DialogSettings } = { [RefreshAlarmLogDialogMode.None]: { - dialogTitle: "", - dialogDescription: "", - applyButtonText: "", - cancelButtonText: "", + dialogTitle: '', + dialogDescription: '', + applyButtonText: '', + cancelButtonText: '', enableMountIdEditor: false, enableUsernameEditor: false, enableExtendedEditor: false, }, [RefreshAlarmLogDialogMode.RefreshAlarmLogTable]: { - dialogTitle: "Do you want to refresh the Alarm Log?", - dialogDescription: "", - applyButtonText: "Yes", - cancelButtonText: "Cancel", + dialogTitle: 'Do you want to refresh the Alarm Log?', + dialogDescription: '', + applyButtonText: 'Yes', + cancelButtonText: 'Cancel', enableMountIdEditor: true, enableUsernameEditor: true, enableExtendedEditor: true, - } -} + }, +}; type RefreshAlarmLogDialogComponentProps = Connect & { mode: RefreshAlarmLogDialogMode; onClose: () => void; }; -type RefreshAlarmLogDialogComponentState = Fault & { isNameValid: boolean, isHostSet: boolean }; +type RefreshAlarmLogDialogComponentState = Fault & { isNameValid: boolean; isHostSet: boolean }; class RefreshAlarmLogDialogComponent extends React.Component { - constructor(props: RefreshAlarmLogDialogComponentProps) { - super(props); - } - render(): JSX.Element { const setting = settings[this.props.mode]; return ( - {setting.dialogTitle} + {setting.dialogTitle} {setting.dialogDescription} - - @@ -110,7 +105,7 @@ class RefreshAlarmLogDialogComponent extends React.Component { this.props.onClose(); - } + }; } export const RefreshAlarmLogDialog = connect(undefined, mapDispatch)(RefreshAlarmLogDialogComponent); diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentAlarmsDialog.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentAlarmsDialog.tsx new file mode 100644 index 000000000..20cd514cd --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentAlarmsDialog.tsx @@ -0,0 +1,113 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ +import * as React from 'react'; + +import Button from '@mui/material/Button'; +import Dialog from '@mui/material/Dialog'; +import DialogActions from '@mui/material/DialogActions'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from '@mui/material/DialogContentText'; +import DialogTitle from '@mui/material/DialogTitle'; + +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; + +import { currentAlarmsReloadAction } from '../handlers/currentAlarmsHandler'; +import { Fault } from '../models/fault'; + +export enum RefreshCurrentAlarmsDialogMode { + None = 'none', + RefreshCurrentAlarmsTable = 'RefreshCurrentAlarmsTable', +} + +const mapDispatch = (dispatcher: IDispatcher) => ({ + refreshCurrentAlarms: () => dispatcher.dispatch(currentAlarmsReloadAction), +}); + +type DialogSettings = { + dialogTitle: string; + dialogDescription: string; + applyButtonText: string; + cancelButtonText: string; + enableMountIdEditor: boolean; + enableUsernameEditor: boolean; + enableExtendedEditor: boolean; +}; + +const settings: { [key: string]: DialogSettings } = { + [RefreshCurrentAlarmsDialogMode.None]: { + dialogTitle: '', + dialogDescription: '', + applyButtonText: '', + cancelButtonText: '', + enableMountIdEditor: false, + enableUsernameEditor: false, + enableExtendedEditor: false, + }, + [RefreshCurrentAlarmsDialogMode.RefreshCurrentAlarmsTable]: { + dialogTitle: 'Do you want to refresh the Current Alarms List?', + dialogDescription: '', + applyButtonText: 'Yes', + cancelButtonText: 'Cancel', + enableMountIdEditor: true, + enableUsernameEditor: true, + enableExtendedEditor: true, + }, +}; + +type RefreshCurrentAlarmsDialogComponentProps = Connect & { + mode: RefreshCurrentAlarmsDialogMode; + onClose: () => void; +}; + +type RefreshCurrentAlarmsDialogComponentState = Fault & { isNameValid: boolean; isHostSet: boolean }; + +class RefreshCurrentAlarmsDialogComponent extends React.Component { + render(): JSX.Element { + const setting = settings[this.props.mode]; + return ( + + {setting.dialogTitle} + + + {setting.dialogDescription} + + + + + + + + ); + } + + private onRefresh = () => { + this.props.refreshCurrentAlarms(); + this.props.onClose(); + }; + + private onCancel = () => { + this.props.onClose(); + }; +} + +export const RefreshCurrentAlarmsDialog = connect(undefined, mapDispatch)(RefreshCurrentAlarmsDialogComponent); +export default RefreshCurrentAlarmsDialog; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentProblemsDialog.tsx b/sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentProblemsDialog.tsx deleted file mode 100644 index e501ec0ad..000000000 --- a/sdnr/wt/odlux/apps/faultApp/src/components/refreshCurrentProblemsDialog.tsx +++ /dev/null @@ -1,117 +0,0 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - * ============LICENSE_END========================================================================== - */ -import * as React from 'react'; - -import Button from '@mui/material/Button'; -import Dialog from '@mui/material/Dialog'; -import DialogActions from '@mui/material/DialogActions'; -import DialogContent from '@mui/material/DialogContent'; -import DialogContentText from '@mui/material/DialogContentText'; -import DialogTitle from '@mui/material/DialogTitle'; - -import { currentProblemsReloadAction } from '../handlers/currentProblemsHandler'; -import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; - -import { Fault } from '../models/fault'; - -export enum RefreshCurrentProblemsDialogMode { - None = "none", - RefreshCurrentProblemsTable = "RefreshCurrentProblemsTable", -} - -const mapDispatch = (dispatcher: IDispatcher) => ({ - refreshCurrentProblems: () => dispatcher.dispatch(currentProblemsReloadAction) -}); - -type DialogSettings = { - dialogTitle: string, - dialogDescription: string, - applyButtonText: string, - cancelButtonText: string, - enableMountIdEditor: boolean, - enableUsernameEditor: boolean, - enableExtendedEditor: boolean, -} - -const settings: { [key: string]: DialogSettings } = { - [RefreshCurrentProblemsDialogMode.None]: { - dialogTitle: "", - dialogDescription: "", - applyButtonText: "", - cancelButtonText: "", - enableMountIdEditor: false, - enableUsernameEditor: false, - enableExtendedEditor: false, - }, - [RefreshCurrentProblemsDialogMode.RefreshCurrentProblemsTable]: { - dialogTitle: "Do you want to refresh the Current Problems List?", - dialogDescription: "", - applyButtonText: "Yes", - cancelButtonText: "Cancel", - enableMountIdEditor: true, - enableUsernameEditor: true, - enableExtendedEditor: true, - } -} - -type RefreshCurrentProblemsDialogComponentProps = Connect & { - mode: RefreshCurrentProblemsDialogMode; - onClose: () => void; -}; - -type RefreshCurrentProblemsDialogComponentState = Fault & { isNameValid: boolean, isHostSet: boolean }; - -class RefreshCurrentProblemsDialogComponent extends React.Component { - constructor(props: RefreshCurrentProblemsDialogComponentProps) { - super(props); - } - - render(): JSX.Element { - const setting = settings[this.props.mode]; - return ( - - {setting.dialogTitle} - - - {setting.dialogDescription} - - - - - - - - ); - } - - private onRefresh = () => { - this.props.refreshCurrentProblems(); - this.props.onClose(); - }; - - private onCancel = () => { - this.props.onClose(); - } -} - -export const RefreshCurrentProblemsDialog = connect(undefined, mapDispatch)(RefreshCurrentProblemsDialogComponent); -export default RefreshCurrentProblemsDialog; \ 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 index 31b8259b2..bdd459669 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/handlers/alarmLogEntriesHandler.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/alarmLogEntriesHandler.ts @@ -15,7 +15,7 @@ * the License. * ============LICENSE_END========================================================================== */ -import { createExternal,IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; +import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch'; import { Fault } from '../models/fault'; diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts index 14634b4c4..0d5a8c70d 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/clearStuckAlarmsHandler.ts @@ -16,21 +16,22 @@ * ============LICENSE_END========================================================================== */ -import { IActionHandler } from "../../../../framework/src/flux/action" -import { AreStuckAlarmsCleared } from "../actions/clearStuckAlarmsAction"; +import { IActionHandler } from '../../../../framework/src/flux/action'; + +import { AreStuckAlarmsCleared } from '../actions/clearStuckAlarmsAction'; export interface IStuckAlarms { - areAlarmsCleared: boolean + areAlarmsCleared: boolean; } const initialState: IStuckAlarms = { - areAlarmsCleared: false -} + areAlarmsCleared: false, +}; export const stuckAlarmHandler: IActionHandler = (state = initialState, action) => { - if (action instanceof AreStuckAlarmsCleared) { - state = { ...state, areAlarmsCleared: action.isBusy } - } + if (action instanceof AreStuckAlarmsCleared) { + state = { ...state, areAlarmsCleared: action.isBusy }; + } - return state; -} \ No newline at end of file + return state; +}; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/currentAlarmsHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/currentAlarmsHandler.ts new file mode 100644 index 000000000..70aa1c27e --- /dev/null +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/currentAlarmsHandler.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 ICurrentAlarmsState extends IExternalTableState { } + +// create eleactic search data fetch handler +const currentAlarmsSearchHandler = createSearchDataHandler('faultcurrent'); + +export const { + actionHandler: currentAlarmsActionHandler, + createActions: createCurrentAlarmsActions, + createProperties: createCurrentAlarmsProperties, + reloadAction: currentAlarmsReloadAction, + + // set value action, to change a value +} = createExternal(currentAlarmsSearchHandler, appState => appState.fault.currentAlarms); + diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/currentProblemsHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/currentProblemsHandler.ts deleted file mode 100644 index 3698a2798..000000000 --- a/sdnr/wt/odlux/apps/faultApp/src/handlers/currentProblemsHandler.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 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 { } - -// create eleactic search data fetch handler -const currentProblemsSearchHandler = createSearchDataHandler('faultcurrent'); - -export const { - actionHandler: currentProblemsActionHandler, - createActions: createCurrentProblemsActions, - createProperties: createCurrentProblemsProperties, - reloadAction: currentProblemsReloadAction, - - // set value action, to change a value -} = createExternal(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 index 2ab1da2ed..e4a19ae5c 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/faultAppRootHandler.ts @@ -17,22 +17,21 @@ */ // main state handler +import { IActionHandler } from '../../../../framework/src/flux/action'; import { combineActionHandler } from '../../../../framework/src/flux/middleware'; - // ** do not remove ** +// eslint-disable-next-line @typescript-eslint/no-unused-vars 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'; import { PanelId } from '../models/panelId'; +import { alarmLogEntriesActionHandler, IAlarmLogEntriesState } from './alarmLogEntriesHandler'; +import { currentAlarmsActionHandler, ICurrentAlarmsState } from './currentAlarmsHandler'; +import { faultStatusHandler, IFaultStatus } from './faultStatusHandler'; +import { faultNotificationsHandler, IFaultNotifications } from './notificationsHandler'; export interface IFaultAppStoreState { - currentProblems: ICurrentProblemsState; + currentAlarms: ICurrentAlarmsState; faultNotifications: IFaultNotifications; alarmLogEntries: IAlarmLogEntriesState; currentOpenPanel: PanelId | null; @@ -44,7 +43,7 @@ const currentOpenPanelHandler: IActionHandler = (state = null, a state = action.panelId; } return state; -} +}; declare module '../../../../framework/src/store/applicationStore' { interface IApplicationStoreState { @@ -53,7 +52,7 @@ declare module '../../../../framework/src/store/applicationStore' { } const actionHandlers = { - currentProblems: currentProblemsActionHandler, + currentAlarms: currentAlarmsActionHandler, faultNotifications: faultNotificationsHandler, alarmLogEntries: alarmLogEntriesActionHandler, currentOpenPanel: currentOpenPanelHandler, diff --git a/sdnr/wt/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts b/sdnr/wt/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts index 6fa95b685..21b033e6a 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/faultStatusHandler.ts @@ -15,24 +15,25 @@ * the License. * ============LICENSE_END========================================================================== */ -import { IActionHandler } from "../../../../framework/src/flux/action"; -import { SetFaultStatusAction } from "../actions/statusActions"; +import { IActionHandler } from '../../../../framework/src/flux/action'; + +import { SetFaultStatusAction } from '../actions/statusActions'; export interface IFaultStatus { - critical: number, - major: number, - minor: number, - warning: number, - isLoadingAlarmStatusChart: boolean, - Connected: number, - Connecting: number, - Disconnected: number, - Mounted: number, - UnableToConnect: number, - Undefined: number, - Unmounted: number, - total: number, - isLoadingConnectionStatusChart: boolean + critical: number; + major: number; + minor: number; + warning: number; + isLoadingAlarmStatusChart: boolean; + Connected: number; + Connecting: number; + Disconnected: number; + Mounted: number; + UnableToConnect: number; + Undefined: number; + Unmounted: number; + total: number; + isLoadingConnectionStatusChart: boolean; } const faultStatusInit: IFaultStatus = { @@ -49,7 +50,7 @@ const faultStatusInit: IFaultStatus = { Undefined: 0, Unmounted: 0, total: 0, - isLoadingConnectionStatusChart: false + isLoadingConnectionStatusChart: false, }; export const faultStatusHandler: IActionHandler = (state = faultStatusInit, action) => { @@ -68,9 +69,9 @@ export const faultStatusHandler: IActionHandler = (state = faultSt Undefined: action.UndefinedCount, Unmounted: action.UnmountedCount, total: action.totalCount, - isLoadingConnectionStatusChart: action.isLoadingConnectionStatusChart - } + isLoadingConnectionStatusChart: action.isLoadingConnectionStatusChart, + }; } return state; -} \ No newline at end of file +}; \ 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 index aa92d2a1c..3d960bfc4 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/handlers/notificationsHandler.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/handlers/notificationsHandler.ts @@ -16,6 +16,7 @@ * ============LICENSE_END========================================================================== */ import { IActionHandler } from '../../../../framework/src/flux/action'; + import { AddFaultNotificationAction, ResetFaultNotificationsAction } from '../actions/notificationActions'; import { FaultAlarmNotification } from '../models/fault'; @@ -26,22 +27,22 @@ export interface IFaultNotifications { const faultNotoficationsInit: IFaultNotifications = { faults: [], - since: new Date() + since: new Date(), }; export const faultNotificationsHandler: IActionHandler = (state = faultNotoficationsInit, action) => { if (action instanceof AddFaultNotificationAction) { state = { ...state, - faults: [...state.faults, action.fault] + faults: [...state.faults, action.fault], }; - } else if (action instanceof ResetFaultNotificationsAction){ + } else if (action instanceof ResetFaultNotificationsAction) { state = { ...state, faults: [], - since: new Date() + since: new Date(), }; } return state; -} \ No newline at end of file +}; \ 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 index 5f38e5fe9..c70253e58 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/models/fault.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/models/fault.ts @@ -25,7 +25,7 @@ export type Fault = { severity: null | 'Warning' | 'Minor' | 'Major' | 'Critical' | 'NonAlarmed'; type: string; sourceType?: string; -} +}; export type FaultAlarmNotification = { id: string; @@ -35,63 +35,63 @@ export type FaultAlarmNotification = { objectId: string; problem: string; severity: string; -} +}; export type FaultAlarmNotificationWS = { - "node-id": string; - "data": { - "counter": number; - "time-stamp": string; - "object-id-ref": string; - "problem": string; - "severity": null | 'Warning' | 'Minor' | 'Major' | 'Critical' | 'NonAlarmed'; + 'node-id': string; + 'data': { + 'counter': number; + 'time-stamp': string; + 'object-id-ref': string; + 'problem': string; + 'severity': null | 'Warning' | 'Minor' | 'Major' | 'Critical' | 'NonAlarmed'; }; - "type": { - "namespace": string; - "revision": string; - "type": string; + 'type': { + 'namespace': string; + 'revision': string; + 'type': string; }; - "event-time": string; -} + 'event-time': string; +}; /** * Fault status return type */ export type FaultsReturnType = { - criticals: number, - majors: number, - minors: number, - warnings: number, - Connected: number, - Connecting: number, - Disconnected: number, - Mounted: number, - UnableToConnect: number, - Undefined: number, - Unmounted: number, - total: number + criticals: number; + majors: number; + minors: number; + warnings: number; + Connected: number; + Connecting: number; + Disconnected: number; + Mounted: number; + UnableToConnect: number; + Undefined: number; + Unmounted: number; + total: number; }; export type FaultType = { - Critical: number, - Major: number, - Minor: number, - Warning: number, - Connected: number, - Connecting: number, - Disconnected: number, - Mounted: number, - UnableToConnect: number, - Undefined: number, - Unmounted: number, - total: number + Critical: number; + Major: number; + Minor: number; + Warning: number; + Connected: number; + Connecting: number; + Disconnected: number; + Mounted: number; + UnableToConnect: number; + Undefined: number; + Unmounted: number; + total: number; }; export type Faults = { - faults: FaultsReturnType, - 'network-element-connections': FaultsReturnType + faults: FaultsReturnType; + 'network-element-connections': FaultsReturnType; }; export type DeletedStuckAlarms = { - nodenames: string[] -} \ No newline at end of file + 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 index 186aa53d9..daebad0e5 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/models/panelId.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/models/panelId.ts @@ -15,4 +15,4 @@ * the License. * ============LICENSE_END========================================================================== */ -export type PanelId = null | "CurrentProblem" | "AlarmNotifications" | "AlarmLog"; \ No newline at end of file +export type PanelId = null | 'CurrentAlarms' | '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 index ff901b097..ca1cfc171 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/pluginFault.tsx @@ -17,92 +17,85 @@ */ // app configuration and main entry point for the app +import React from 'react'; +import { Redirect, Route, RouteComponentProps, Switch, withRouter } from 'react-router-dom'; -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 { connect, Connect, IDispatcher } from '../../../framework/src/flux/connect'; import applicationManager from '../../../framework/src/services/applicationManager'; -import { subscribe, IFormatedMessage } from '../../../framework/src/services/notificationService'; -import { IApplicationStoreState } from "../../../framework/src/store/applicationStore"; - +import { IFormatedMessage, subscribe } from '../../../framework/src/services/notificationService'; +import { IApplicationStoreState } from '../../../framework/src/store/applicationStore'; + +import { AddFaultNotificationAction } from './actions/notificationActions'; +import { SetPanelAction } from './actions/panelChangeActions'; +import { refreshFaultStatusAsyncAction, SetFaultStatusAction } from './actions/statusActions'; +import DashboardHome from './components/dashboardHome'; +import { FaultStatus } from './components/faultStatus'; +import { createCurrentAlarmsActions, createCurrentAlarmsProperties, currentAlarmsReloadAction } from './handlers/currentAlarmsHandler'; import { faultAppRootHandler } from './handlers/faultAppRootHandler'; -import { FaultApplication } from "./views/faultApplication"; - -import { FaultAlarmNotificationWS } 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, SetFaultStatusAction } from "./actions/statusActions"; +import { FaultAlarmNotificationWS } from './models/fault'; +import { PanelId } from './models/panelId'; +import { FaultApplication } from './views/faultApplication'; -import DashboardHome from "./components/dashboardHome"; +const appIcon = require('./assets/icons/faultAppIcon.svg'); // select app icon let currentMountId: string | undefined = undefined; let currentSeverity: string | undefined = undefined; let refreshInterval: ReturnType | null = null; const mapProps = (state: IApplicationStoreState) => ({ - currentProblemsProperties: createCurrentProblemsProperties(state), + currentAlarmsProperties: createCurrentAlarmsProperties(state), }); -const mapDisp = (dispatcher: IDispatcher) => ({ - currentProblemsActions: createCurrentProblemsActions(dispatcher.dispatch, true), +const mapDispatch = (dispatcher: IDispatcher) => ({ + currentAlarmsActions: createCurrentAlarmsActions(dispatcher.dispatch, true), setCurrentPanel: (panelId: PanelId) => dispatcher.dispatch(new SetPanelAction(panelId)), }); -const FaultApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ mountId?: string }> & Connect) => { +const FaultApplicationRouteAdapter = connect(mapProps, mapDispatch)((props: RouteComponentProps<{ mountId?: string }> & Connect) => { 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); - if (!props.currentProblemsProperties.showFilter) { - props.currentProblemsActions.onToggleFilter(false); - props.currentProblemsActions.onRefresh(); - } - else - props.currentProblemsActions.onRefresh(); + props.setCurrentPanel('CurrentAlarms'); + props.currentAlarmsActions.onFilterChanged('nodeId', currentMountId); + if (!props.currentAlarmsProperties.showFilter) { + props.currentAlarmsActions.onToggleFilter(false); + props.currentAlarmsActions.onRefresh(); + } else + props.currentAlarmsActions.onRefresh(); } }); } return ( - ) + ); }); -const FaulttApplicationAlarmStatusRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ severity?: string }> & Connect) => { +const FaultApplicationAlarmStatusRouteAdapter = connect(mapProps, mapDispatch)((props: RouteComponentProps<{ severity?: string }> & Connect) => { if (currentSeverity !== props.match.params.severity) { currentSeverity = props.match.params.severity || undefined; window.setTimeout(() => { if (currentSeverity) { - props.setCurrentPanel("CurrentProblem"); - props.currentProblemsActions.onFilterChanged("severity", currentSeverity); - if (!props.currentProblemsProperties.showFilter) { - props.currentProblemsActions.onToggleFilter(false); - props.currentProblemsActions.onRefresh(); - } - else - props.currentProblemsActions.onRefresh(); + props.setCurrentPanel('CurrentAlarms'); + props.currentAlarmsActions.onFilterChanged('severity', currentSeverity); + if (!props.currentAlarmsProperties.showFilter) { + props.currentAlarmsActions.onToggleFilter(false); + props.currentAlarmsActions.onRefresh(); + } else + props.currentAlarmsActions.onRefresh(); } }); } return ( - ) + ); }); const App = withRouter((props: RouteComponentProps) => ( - + @@ -110,54 +103,48 @@ const App = withRouter((props: RouteComponentProps) => ( export function register() { const applicationApi = applicationManager.registerApplication({ - name: "fault", - icon: faBell, + name: 'fault', + icon: appIcon, rootComponent: App, rootActionHandler: faultAppRootHandler, statusBarElement: FaultStatus, dashbaordElement: DashboardHome, - menuEntry: "Fault" + menuEntry: 'Fault', }); let counter = 0; // subscribe to the websocket notifications - subscribe("problem-notification", (fault => { + subscribe('problem-notification', (fault => { const store = applicationApi && applicationApi.applicationStore; if (fault && store) { store.dispatch(new AddFaultNotificationAction({ id: String(counter++), - nodeName: fault["node-id"], + nodeName: fault['node-id'], counter: +fault.data.counter, - objectId: fault.data["object-id-ref"], + objectId: fault.data['object-id-ref'], problem: fault.data.problem, severity: fault.data.severity || '', - timeStamp: fault.data["time-stamp"], + timeStamp: fault.data['time-stamp'], })); } })); applicationApi.applicationStoreInitialized.then(store => { - store.dispatch(currentProblemsReloadAction); + store.dispatch(currentAlarmsReloadAction); }); applicationApi.applicationStoreInitialized.then(store => { store.dispatch(refreshFaultStatusAsyncAction); }); - applicationApi.loginEvent.addHandler(e=>{ - refreshInterval = startRefreshInterval() as any; - }) - - applicationApi.logoutEvent.addHandler(e=>{ + applicationApi.logoutEvent.addHandler(()=>{ applicationApi.applicationStoreInitialized.then(store => { store.dispatch(new SetFaultStatusAction(0, 0, 0, 0, false, 0, 0, 0, 0, 0, 0, 0, 0, false)); clearInterval(refreshInterval!); }); - }) - - + }); function startRefreshInterval() { const refreshFaultStatus = window.setInterval(() => { @@ -170,4 +157,7 @@ export function register() { return refreshFaultStatus; } + applicationApi.loginEvent.addHandler(()=>{ + refreshInterval = startRefreshInterval() as any; + }); } diff --git a/sdnr/wt/odlux/apps/faultApp/src/services/faultStatusService.ts b/sdnr/wt/odlux/apps/faultApp/src/services/faultStatusService.ts index 880ed7715..0c7a215f8 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/services/faultStatusService.ts +++ b/sdnr/wt/odlux/apps/faultApp/src/services/faultStatusService.ts @@ -15,14 +15,15 @@ * the License. * ============LICENSE_END========================================================================== */ -import { requestRest } from "../../../../framework/src/services/restService"; -import { Result, SingeResult } from "../../../../framework/src/models/elasticSearch"; -import { FaultType, Faults, DeletedStuckAlarms } from "../models/fault"; +import { Result } from '../../../../framework/src/models/elasticSearch'; +import { requestRest } from '../../../../framework/src/services/restService'; + +import { Faults, FaultType } from '../models/fault'; export const getFaultStateFromDatabase = async (): Promise => { const path = 'rests/operations/data-provider:read-status'; - const result = await requestRest>(path, { method: "POST" }); + const result = await requestRest>(path, { method: 'POST' }); let faultType: FaultType = { Critical: 0, @@ -36,34 +37,33 @@ export const getFaultStateFromDatabase = async (): Promise => UnableToConnect: 0, Undefined: 0, Unmounted: 0, - total: 0 - } + total: 0, + }; let faults: Faults[] | null = null; - if (result && result["data-provider:output"] && result["data-provider:output"].data) { - faults = result["data-provider:output"].data; + if (result && result['data-provider:output'] && result['data-provider:output'].data) { + faults = result['data-provider:output'].data; faultType = { Critical: faults[0].faults.criticals, Major: faults[0].faults.majors, Minor: faults[0].faults.minors, Warning: faults[0].faults.warnings, - Connected: faults[0]["network-element-connections"].Connected, - Connecting: faults[0]["network-element-connections"].Connecting, - Disconnected: faults[0]["network-element-connections"].Disconnected, - Mounted: faults[0]["network-element-connections"].Mounted, - UnableToConnect: faults[0]["network-element-connections"].UnableToConnect, - Undefined: faults[0]["network-element-connections"].Undefined, - Unmounted: faults[0]["network-element-connections"].Unmounted, - total: faults[0]["network-element-connections"].total, - } + Connected: faults[0]['network-element-connections'].Connected, + Connecting: faults[0]['network-element-connections'].Connecting, + Disconnected: faults[0]['network-element-connections'].Disconnected, + Mounted: faults[0]['network-element-connections'].Mounted, + UnableToConnect: faults[0]['network-element-connections'].UnableToConnect, + Undefined: faults[0]['network-element-connections'].Undefined, + Unmounted: faults[0]['network-element-connections'].Unmounted, + total: faults[0]['network-element-connections'].total, + }; } return faultType; -} +}; export const clearStuckAlarms = async (nodeNames: string[]) => { - const path = 'rests/operations/devicemanager:clear-current-fault-by-nodename' - const result = await requestRest(path, { method: 'Post', body: JSON.stringify({ input: { nodenames: nodeNames } }) }) + const path = 'rests/operations/devicemanager:clear-current-fault-by-nodename'; + const result = await requestRest(path, { method: 'Post', body: JSON.stringify({ input: { nodenames: nodeNames } }) }); return result; - -} \ No newline at end of file +}; \ 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 index b9cd5e4da..b9f115ed7 100644 --- a/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx +++ b/sdnr/wt/odlux/apps/faultApp/src/views/faultApplication.tsx @@ -15,44 +15,37 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; -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 Refresh from '@mui/icons-material/Refresh'; +import Sync from '@mui/icons-material/Sync'; +import { AppBar, Tab, Tabs } from '@mui/material'; +import { ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; 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, AppBar, Tabs, Tab } from '@mui/material'; -import Sync from '@mui/icons-material/Sync'; -import Refresh from '@mui/icons-material/Refresh'; - import ClearStuckAlarmsDialog, { ClearStuckAlarmsDialogMode } from '../components/clearStuckAlarmsDialog'; import RefreshAlarmLogDialog, { RefreshAlarmLogDialogMode } from '../components/refreshAlarmLogDialog'; -import RefreshCurrentProblemsDialog, { RefreshCurrentProblemsDialogMode } from '../components/refreshCurrentProblemsDialog'; +import RefreshCurrentAlarmsDialog, { RefreshCurrentAlarmsDialogMode } from '../components/refreshCurrentAlarmsDialog'; +import { alarmLogEntriesReloadAction, createAlarmLogEntriesActions, createAlarmLogEntriesProperties } from '../handlers/alarmLogEntriesHandler'; +import { createCurrentAlarmsActions, createCurrentAlarmsProperties, currentAlarmsReloadAction } from '../handlers/currentAlarmsHandler'; +import { Fault, FaultAlarmNotification } from '../models/fault'; +import { PanelId } from '../models/panelId'; const mapProps = (state: IApplicationStoreState) => ({ panelId: state.fault.currentOpenPanel, - currentProblemsProperties: createCurrentProblemsProperties(state), + currentAlarmsProperties: createCurrentAlarmsProperties(state), faultNotifications: state.fault.faultNotifications, - alarmLogEntriesProperties: createAlarmLogEntriesProperties(state) + alarmLogEntriesProperties: createAlarmLogEntriesProperties(state), }); const mapDisp = (dispatcher: IDispatcher) => ({ - currentProblemsActions: createCurrentProblemsActions(dispatcher.dispatch), + currentAlarmsActions: createCurrentAlarmsActions(dispatcher.dispatch), alarmLogEntriesActions: createAlarmLogEntriesActions(dispatcher.dispatch), - reloadCurrentProblems: () => dispatcher.dispatch(currentProblemsReloadAction), + reloadCurrentAlarms: () => dispatcher.dispatch(currentAlarmsReloadAction), reloadAlarmLogEntries: () => dispatcher.dispatch(alarmLogEntriesReloadAction), switchActivePanel: (panelId: PanelId) => { dispatcher.dispatch(setPanelAction(panelId)); @@ -62,63 +55,59 @@ const mapDisp = (dispatcher: IDispatcher) => ({ type FaultApplicationComponentProps = RouteComponentProps & Connect; type FaultApplicationState = { - clearAlarmDialogMode: ClearStuckAlarmsDialogMode, - stuckAlarms: string[], - refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode, - refreshCurrentProblemsEditorMode: RefreshCurrentProblemsDialogMode -} + clearAlarmDialogMode: ClearStuckAlarmsDialogMode; + stuckAlarms: string[]; + refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode; + refreshCurrentAlarmsEditorMode: RefreshCurrentAlarmsDialogMode; +}; const FaultTable = MaterialTable as MaterialTableCtorType; const FaultAlarmNotificationTable = MaterialTable as MaterialTableCtorType; -let currentProblemsInitalSorted = false; +let currentAlarmsInitalSorted = false; let alarmLogInitialSorted = false; -class FaultApplicationComponent extends React.Component{ - - /** - * - */ +class FaultApplicationComponent extends React.Component { constructor(props: FaultApplicationComponentProps) { super(props); this.state = { clearAlarmDialogMode: ClearStuckAlarmsDialogMode.None, stuckAlarms: [], refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode.None, - refreshCurrentProblemsEditorMode: RefreshCurrentProblemsDialogMode.None - } + refreshCurrentAlarmsEditorMode: RefreshCurrentAlarmsDialogMode.None, + }; } onDialogClose = () => { - this.setState({ clearAlarmDialogMode: ClearStuckAlarmsDialogMode.None, stuckAlarms: [] }) - } + 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 }) - } + const stuckAlarms = [...new Set(this.props.currentAlarmsProperties.rows.map(item => item.nodeId))]; + this.setState({ clearAlarmDialogMode: ClearStuckAlarmsDialogMode.Show, stuckAlarms: stuckAlarms }); + }; private onHandleTabChange = (event: React.SyntheticEvent, newValue: PanelId) => { this.onToggleTabs(newValue); - } + }; private onToggleTabs = (panelId: PanelId) => { const nextActivePanel = panelId; this.props.switchActivePanel(nextActivePanel); switch (nextActivePanel) { - case 'CurrentProblem': - if (!currentProblemsInitalSorted) { - currentProblemsInitalSorted = true; - this.props.currentProblemsActions.onHandleExplicitRequestSort("timestamp", "desc"); + case 'CurrentAlarms': + if (!currentAlarmsInitalSorted) { + currentAlarmsInitalSorted = true; + this.props.currentAlarmsActions.onHandleExplicitRequestSort('timestamp', 'desc'); } else { - this.props.reloadCurrentProblems(); + this.props.reloadCurrentAlarms(); } break; case 'AlarmLog': if (!alarmLogInitialSorted) { alarmLogInitialSorted = true; - this.props.alarmLogEntriesActions.onHandleExplicitRequestSort("timestamp", "desc"); + this.props.alarmLogEntriesActions.onHandleExplicitRequestSort('timestamp', 'desc'); } else { this.props.reloadAlarmLogEntries(); } @@ -136,27 +125,27 @@ class FaultApplicationComponent extends React.Component { + const refreshCurrentAlarmsAction = { + icon: Refresh, tooltip: 'Refresh Current Alarms List', ariaLabel:'refresh', onClick: () => { this.setState({ - refreshCurrentProblemsEditorMode: RefreshCurrentProblemsDialogMode.RefreshCurrentProblemsTable + refreshCurrentAlarmsEditorMode: RefreshCurrentAlarmsDialogMode.RefreshCurrentAlarmsTable, }); - } + }, }; const refreshAlarmLogAction = { icon: Refresh, tooltip: 'Refresh Alarm log table', ariaLabel:'refresh', onClick: () => { this.setState({ - refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode.RefreshAlarmLogTable + refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode.RefreshAlarmLogTable, }); - } + }, }; - const areFaultsAvailable = this.props.currentProblemsProperties.rows && this.props.currentProblemsProperties.rows.length > 0 - const customActions = areFaultsAvailable ? [clearAlarmsAction, refreshCurrentProblemsAction] : [refreshCurrentProblemsAction]; + const areFaultsAvailable = this.props.currentAlarmsProperties.rows && this.props.currentAlarmsProperties.rows.length > 0; + const customActions = areFaultsAvailable ? [clearAlarmsAction, refreshCurrentAlarmsAction] : [refreshCurrentAlarmsAction]; const { panelId: activePanelId } = this.props; @@ -164,26 +153,26 @@ class FaultApplicationComponent extends React.Component - + { - activePanelId === 'CurrentProblem' && + activePanelId === 'CurrentAlarms' && <> - - + } @@ -191,12 +180,12 @@ class FaultApplicationComponent extends React.Component } @@ -205,13 +194,13 @@ class FaultApplicationComponent extends React.Component } - ) - - }; + ); + } public componentDidMount() { if (this.props.panelId === null) { //set default tab if none is set - this.onToggleTabs("CurrentProblem"); - }else{ + this.onToggleTabs('CurrentAlarms'); + } else { this.onToggleTabs(this.props.panelId); } } - private renderIcon = (props: { rowData: Fault | FaultAlarmNotification }) => { - return ( - - ); - }; + // private renderIcon = (props: { rowData: Fault | FaultAlarmNotification }) => { + // return ( + // + // ); + // }; private onCloseRefreshAlarmLogDialog = () => { this.setState({ - refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode.None + refreshAlarmLogEditorMode: RefreshAlarmLogDialogMode.None, }); - } - private onCloseRefreshCurrentProblemsDialog = () => { + }; + + private onCloseRefreshCurrentAlarmsDialog = () => { this.setState({ - refreshCurrentProblemsEditorMode: RefreshCurrentProblemsDialogMode.None + refreshCurrentAlarmsEditorMode: RefreshCurrentAlarmsDialogMode.None, }); - } + }; } export const FaultApplication = withRouter(connect(mapProps, mapDisp)(FaultApplicationComponent)); diff --git a/sdnr/wt/odlux/apps/faultApp/tsconfig.json b/sdnr/wt/odlux/apps/faultApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/faultApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/faultApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/faultApp/webpack.config.js b/sdnr/wt/odlux/apps/faultApp/webpack.config.js index d34d31c91..bc26de1cb 100644 --- a/sdnr/wt/odlux/apps/faultApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/faultApp/webpack.config.js @@ -57,6 +57,16 @@ module.exports = (env) => { use: [{ loader: "babel-loader" }] + },{ + //don't minify images + test: /\.(png|gif|jpg|svg)$/, + use: [{ + loader: 'url-loader', + options: { + limit: 10, + name: './images/[name].[ext]' + } + }] }] }, optimization: { @@ -125,27 +135,27 @@ module.exports = (env) => { }, proxy: { "/oauth2/": { - target: "http://sdnr:8181", + target: "http://sdnc-web:8080", secure: false }, "/database/": { - target: "http://sdnr:8181", + target: "http://sdnc-web:8080", secure: false }, "/restconf/": { - target: "http://sdnr:8181", + target: "http://sdnc-web:8080", secure: false }, "/rests/": { - target: "http://sdnr:8181", + target: "http://sdnc-web:8080", secure: false }, "/help/": { - target: "http://sdnr:8181", + target: "http://sdnc-web:8080", secure: false }, "/websocket": { - target: "http://sdnr:8181", + target: "http://sdnc-web:8080", ws: true, changeOrigin: true, secure: false diff --git a/sdnr/wt/odlux/apps/helpApp/pom.xml b/sdnr/wt/odlux/apps/helpApp/pom.xml index 9da030913..07ac09164 100644 --- a/sdnr/wt/odlux/apps/helpApp/pom.xml +++ b/sdnr/wt/odlux/apps/helpApp/pom.xml @@ -19,6 +19,7 @@ ~ ============LICENSE_END======================================================= ~ --> + 4.0.0 @@ -139,7 +140,7 @@ initialize - v12.13.0 + v12.22.0 v1.22.10 diff --git a/sdnr/wt/odlux/apps/helpApp/src/assets/icons/helpAppIcon.svg b/sdnr/wt/odlux/apps/helpApp/src/assets/icons/helpAppIcon.svg new file mode 100644 index 000000000..298eaa162 --- /dev/null +++ b/sdnr/wt/odlux/apps/helpApp/src/assets/icons/helpAppIcon.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + diff --git a/sdnr/wt/odlux/apps/helpApp/src/components/helpStatus.tsx b/sdnr/wt/odlux/apps/helpApp/src/components/helpStatus.tsx index fd4cd3fa4..985b404a7 100644 --- a/sdnr/wt/odlux/apps/helpApp/src/components/helpStatus.tsx +++ b/sdnr/wt/odlux/apps/helpApp/src/components/helpStatus.tsx @@ -15,7 +15,8 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; +import { withRouter, RouteComponentProps } from 'react-router'; import { Theme } from '@mui/material/styles'; import { WithStyles } from '@mui/styles'; @@ -23,13 +24,12 @@ import withStyles from '@mui/styles/withStyles'; import createStyles from '@mui/styles/createStyles'; 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 '@mui/material/Typography'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faQuestionCircle } from '@fortawesome/free-solid-svg-icons'; -import { withRouter, RouteComponentProps } from 'react-router'; + +import { connect, Connect } from '../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; const styles = (theme: Theme) => createStyles({ icon: { diff --git a/sdnr/wt/odlux/apps/helpApp/src/handlers/helpAppRootHandler.ts b/sdnr/wt/odlux/apps/helpApp/src/handlers/helpAppRootHandler.ts index cc6a98488..29e07959a 100644 --- a/sdnr/wt/odlux/apps/helpApp/src/handlers/helpAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/helpApp/src/handlers/helpAppRootHandler.ts @@ -18,9 +18,10 @@ // 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'; +import { TocTreeNode } from '../models/tocNode'; export interface IHelpAppStoreState { busy: boolean; diff --git a/sdnr/wt/odlux/apps/helpApp/src/plugin.tsx b/sdnr/wt/odlux/apps/helpApp/src/plugin.tsx index 50a264b15..5d860e530 100644 --- a/sdnr/wt/odlux/apps/helpApp/src/plugin.tsx +++ b/sdnr/wt/odlux/apps/helpApp/src/plugin.tsx @@ -17,14 +17,12 @@ */ // app configuration and main entry point for the app -import * as React from "react"; +import 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 { connect, Connect, IDispatcher } from '../../../framework/src/flux/connect'; import { requestTocAsyncAction, requestDocumentAsyncActionCreator } from "./actions/helpActions"; import { helpAppRootHandler } from './handlers/helpAppRootHandler'; @@ -35,11 +33,13 @@ import { HelpStatus } from "./components/helpStatus"; import '!style-loader!css-loader!highlight.js/styles/default.css'; import HelpTocApp from "./views/helpTocApp"; +const appIcon = require('./assets/icons/helpAppIcon.svg'); // select app icon + const mapProps = (state: IApplicationStoreState) => ({ }); -const mapDisp = (dispatcher: IDispatcher) => ({ +const mapDispatch = (dispatcher: IDispatcher) => ({ requestDocument: (path: string) => { dispatcher.dispatch(requestDocumentAsyncActionCreator(path)); } @@ -47,7 +47,7 @@ const mapDisp = (dispatcher: IDispatcher) => ({ let currentHelpPath: string | undefined = undefined; -const HelpApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ '0'?: string }> & Connect) => { +const HelpApplicationRouteAdapter = connect(mapProps, mapDispatch)((props: RouteComponentProps<{ '0'?: string }> & Connect) => { if (currentHelpPath !== props.match.params["0"]) { // route parameter has changed @@ -76,7 +76,7 @@ const App = withRouter((props: RouteComponentProps) => ( export async function register() { const applicationApi = applicationManager.registerApplication({ name: "help", - icon: faFirstAid, + icon: appIcon, rootComponent: App, rootActionHandler: helpAppRootHandler, statusBarElement: HelpStatus, @@ -84,7 +84,7 @@ export async function register() { //subMenuEntry: SubMenuEntry }); - // start the initial toc request after the application store is initalized + // start the initial toc request after the application store is initialized const store = await applicationApi.applicationStoreInitialized; store.dispatch(requestTocAsyncAction); diff --git a/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx b/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx index eab44b4ca..5940517b4 100644 --- a/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx +++ b/sdnr/wt/odlux/apps/helpApp/src/views/helpApplication.tsx @@ -15,13 +15,13 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import 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 { connect, Connect } from '../../../../framework/src/flux/connect'; import { Markdown } from "../components/markdown"; diff --git a/sdnr/wt/odlux/apps/helpApp/src/views/helpTocApp.tsx b/sdnr/wt/odlux/apps/helpApp/src/views/helpTocApp.tsx index e1de4d0f5..6a6a89123 100644 --- a/sdnr/wt/odlux/apps/helpApp/src/views/helpTocApp.tsx +++ b/sdnr/wt/odlux/apps/helpApp/src/views/helpTocApp.tsx @@ -16,10 +16,10 @@ * ============LICENSE_END========================================================================== */ -import connect, { Connect, IDispatcher } from "../../../../framework/src/flux/connect"; +import React from 'react' +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"; diff --git a/sdnr/wt/odlux/apps/helpApp/tsconfig.json b/sdnr/wt/odlux/apps/helpApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/helpApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/helpApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/helpApp/webpack.config.js b/sdnr/wt/odlux/apps/helpApp/webpack.config.js index b069c2a31..a48f7b976 100644 --- a/sdnr/wt/odlux/apps/helpApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/helpApp/webpack.config.js @@ -74,6 +74,16 @@ module.exports = (env) => { plugins: () => [autoprefixer] } }] + }, { + //don't minify images + test: /\.(png|gif|jpg|svg)$/, + use: [{ + loader: 'url-loader', + options: { + limit: 10, + name: './images/[name].[ext]' + } + }] }] }, diff --git a/sdnr/wt/odlux/apps/inventoryApp/pom.xml b/sdnr/wt/odlux/apps/inventoryApp/pom.xml index e39e26744..c6ec595c0 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/pom.xml +++ b/sdnr/wt/odlux/apps/inventoryApp/pom.xml @@ -19,6 +19,7 @@ ~ ============LICENSE_END======================================================= ~ --> + 4.0.0 @@ -139,7 +140,7 @@ initialize - v12.13.0 + v12.22.0 v1.22.10 diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryDeviceListActions.ts b/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryDeviceListActions.ts new file mode 100644 index 000000000..710959a2a --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryDeviceListActions.ts @@ -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 { Action } from '../../../../framework/src/flux/action'; +import { Dispatch } from '../../../../framework/src/flux/store'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import { InventoryDeviceListType } from '../models/inventoryDeviceListType'; +import { inventoryService } from '../services/inventoryService'; + +/** + * Represents the base action. + */ +export class BaseAction extends Action { } + +/** + * Represents an action causing the store to load all nodes. + */ +export class LoadAllInventoryDeviceListAction extends BaseAction { } + +/** + * Represents an action causing the store to update all nodes. + */ +export class AllInventoryDeviceListLoadedAction extends BaseAction { + /** + * Initialize this instance. + * + * @param inventoryDeviceList All the distinct nodes from the Inventory database. + */ + constructor(public inventoryDeviceList: InventoryDeviceListType[] | null, public error?: string) { + super(); + } +} + +/** + * Represents an asynchronous thunk action to load all nodes. + */ +export const loadAllInventoryDeviceListAsync = async (dispatch: Dispatch) => { + dispatch(new LoadAllInventoryDeviceListAction()); + const inventoryDeviceList: InventoryDeviceListType[] = (await inventoryService.getInventoryDeviceList().then(ne => + (ne))) || []; + return inventoryDeviceList && dispatch(new AllInventoryDeviceListLoadedAction(inventoryDeviceList)); +}; + diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryTreeActions.ts b/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryTreeActions.ts index c09b669a1..2c6a0ed65 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryTreeActions.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/actions/inventoryTreeActions.ts @@ -16,14 +16,13 @@ * ============LICENSE_END========================================================================== */ +import { AddErrorInfoAction } from '../../../../framework/src/actions/errorActions'; +import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions'; import { Action } from '../../../../framework/src/flux/action'; import { Dispatch } from '../../../../framework/src/flux/store'; -import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import { InventoryType, InventoryTreeNode, TreeDemoItem } from '../models/inventory'; +import { InventoryTreeNode, InventoryType, TreeDemoItem } from '../models/inventory'; import { inventoryService } from '../services/inventoryService'; -import { AddErrorInfoAction } from '../../../../framework/src/actions/errorActions'; -import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions'; /** * Represents the base action. @@ -38,7 +37,7 @@ export class SetBusyAction extends BaseAction { } export class SetSearchTextAction extends BaseAction { - constructor(public searchTerm: string = "") { + constructor(public searchTerm: string = '') { super(); } @@ -65,40 +64,38 @@ export class UpdateExpandedNodesAction extends BaseAction { } } -export const setSearchTermAction = (searchTerm: string) => (dispatch: Dispatch, getState: () => IApplicationStoreState) =>{ +export const setSearchTermAction = (searchTerm: string) => (dispatch: Dispatch) =>{ dispatch(new SetSearchTextAction(searchTerm)); -} +}; -export const updateInventoryTreeAsyncAction = (mountId: string, searchTerm?: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => { +export const updateInventoryTreeAsyncAction = (mountId: string, searchTerm?: string) => async (dispatch: Dispatch) => { dispatch(new SetBusyAction(true)); dispatch(new SetSearchTextAction(searchTerm)); try { const result = await inventoryService.getInventoryTree(mountId, searchTerm); if (!result) { - dispatch(new AddErrorInfoAction({ title: "Error", message: `Could not load inventory tree for [${mountId}]. Please check you connection to the server and try later.` })); - dispatch(new NavigateToApplication("inventory")); + dispatch(new AddErrorInfoAction({ title: 'Error', message: `Could not load inventory tree for [${mountId}]. Please check you connection to the server and try later.` })); + dispatch(new NavigateToApplication('inventory')); } else { dispatch(new UpdateInventoryTreeAction(result)); } } catch (err) { - throw new Error("Could not load inventory tree from server."); - } - finally { + throw new Error('Could not load inventory tree from server.'); + } finally { dispatch(new SetBusyAction(false)); } }; -export const selectInventoryNodeAsyncAction = (nodeId: string) => async (dispatch: Dispatch, getState: () => IApplicationStoreState) => { +export const selectInventoryNodeAsyncAction = (nodeId: string) => async (dispatch: Dispatch) => { dispatch(new SetBusyAction(true)); try { const result = await inventoryService.getInventoryEntry(nodeId); - if (!result) throw new Error("Could not load inventory tree from server."); + if (!result) throw new Error('Could not load inventory tree from server.'); dispatch(new UpdateSelectedNodeAction(result)); } catch (err) { - throw new Error("Could not load inventory tree from server."); - } - finally { + throw new Error('Could not load inventory tree from server.'); + } finally { dispatch(new SetBusyAction(false)); } }; diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/actions/panelActions.ts b/sdnr/wt/odlux/apps/inventoryApp/src/actions/panelActions.ts index 10fde8c1d..d66608296 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/actions/panelActions.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/actions/panelActions.ts @@ -16,16 +16,16 @@ * ============LICENSE_END========================================================================== */ -import { Action } from "../../../../framework/src/flux/action"; -import { PanelId } from "models/panelId"; +import { Action } from '../../../../framework/src/flux/action'; +import { PanelId } from '../models/panelId'; export class SetPanelAction extends Action { - constructor(public panelId: PanelId) { - super(); - } + constructor(public panelId: PanelId) { + super(); } +} export const setPanelAction = (panelId: PanelId) => { - return new SetPanelAction(panelId); - } \ No newline at end of file + return new SetPanelAction(panelId); +}; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/assets/icons/inventoryAppIcon.svg b/sdnr/wt/odlux/apps/inventoryApp/src/assets/icons/inventoryAppIcon.svg new file mode 100644 index 000000000..507a835ab --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/assets/icons/inventoryAppIcon.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + + + + + + diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/components/refreshInventoryDialog.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/components/refreshInventoryDialog.tsx index 04658a319..027622249 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/components/refreshInventoryDialog.tsx +++ b/sdnr/wt/odlux/apps/inventoryApp/src/components/refreshInventoryDialog.tsx @@ -24,78 +24,74 @@ import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { inventoryElementsReloadAction } from '../handlers/inventoryElementsHandler'; -import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; import { InventoryType } from '../models/inventory'; export enum RefreshInventoryDialogMode { - None = "none", - RefreshInventoryTable = "RefreshInventoryTable", + None = 'none', + RefreshInventoryTable = 'RefreshInventoryTable', } const mapDispatch = (dispatcher: IDispatcher) => ({ - refreshInventory: () => dispatcher.dispatch(inventoryElementsReloadAction) + refreshInventory: () => dispatcher.dispatch(inventoryElementsReloadAction), }); type DialogSettings = { - dialogTitle: string, - dialogDescription: string, - applyButtonText: string, - cancelButtonText: string, - enableMountIdEditor: boolean, - enableUsernameEditor: boolean, - enableExtendedEditor: boolean, -} + dialogTitle: string; + dialogDescription: string; + applyButtonText: string; + cancelButtonText: string; + enableMountIdEditor: boolean; + enableUsernameEditor: boolean; + enableExtendedEditor: boolean; +}; const settings: { [key: string]: DialogSettings } = { [RefreshInventoryDialogMode.None]: { - dialogTitle: "", - dialogDescription: "", - applyButtonText: "", - cancelButtonText: "", + dialogTitle: '', + dialogDescription: '', + applyButtonText: '', + cancelButtonText: '', enableMountIdEditor: false, enableUsernameEditor: false, enableExtendedEditor: false, }, [RefreshInventoryDialogMode.RefreshInventoryTable]: { - dialogTitle: "Do you want to refresh the Inventory table?", - dialogDescription: "", - applyButtonText: "Yes", - cancelButtonText: "Cancel", + dialogTitle: 'Do you want to refresh the Inventory table?', + dialogDescription: '', + applyButtonText: 'Yes', + cancelButtonText: 'Cancel', enableMountIdEditor: true, enableUsernameEditor: true, enableExtendedEditor: true, - } -} + }, +}; type RefreshInventoryDialogComponentProps = Connect & { mode: RefreshInventoryDialogMode; onClose: () => void; }; -type RefreshInventoryDialogComponentState = InventoryType & { isNameValid: boolean, isHostSet: boolean }; +type RefreshInventoryDialogComponentState = InventoryType & { isNameValid: boolean; isHostSet: boolean }; class RefreshInventoryDialogComponent extends React.Component { - constructor(props: RefreshInventoryDialogComponentProps) { - super(props); - } - render(): JSX.Element { const setting = settings[this.props.mode]; return ( - {setting.dialogTitle} + {setting.dialogTitle} {setting.dialogDescription} - - @@ -110,7 +106,7 @@ class RefreshInventoryDialogComponent extends React.Component { this.props.onClose(); - } + }; } export const RefreshInventoryDialog = connect(undefined, mapDispatch)(RefreshInventoryDialogComponent); diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/fakeData/index.ts b/sdnr/wt/odlux/apps/inventoryApp/src/fakeData/index.ts index 46827e842..136b908dd 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/fakeData/index.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/fakeData/index.ts @@ -16,11 +16,10 @@ * ============LICENSE_END========================================================================== */ -import { InventoryTreeNode, InventoryType } from "models/inventory"; import { convertPropertyNames, replaceHyphen } from "../../../../framework/src/utilities/yangHelper"; -// Tree mittels tree-level und parent UUID (incl) -// einzelabfrage mit db-id +import { InventoryTreeNode, InventoryType } from "../models/inventory"; + const data = [ { "manufacturer-identifier": "ONF-Wireless-Transport", "version": "a2.module-newest", "uuid": "a2.module-1.1.1.5", "part-type-id": "3FE25774AA01", "model-identifier": "VAUIAEYAAA", "tree-level": 2, "node-id": "robot_sim_2_equipment", "description": "WS/CORE-MAIN/a2.module#5", "type-name": "a2.module", "serial": "0003548168", "id": "robot_sim_2_equipment/a2.module-1.1.1.5", "parent-uuid": "CARD-1.1.1.0", "contained-holder": ["SUBRACK-1.15.0.0"], "date": "2005-11-09T00:00:00.0Z" }, { "manufacturer-identifier": "SAN", "version": "234", "uuid": "CARD-1.1.6.0", "part-type-id": "part-number-12", "model-identifier": "model-id-12", "tree-level": 1, "node-id": "robot_sim_2_equipment", "description": "WS/p8.module", "type-name": "p8.module", "serial": "serial-number-124", "id": "robot_sim_2_equipment/CARD-1.1.6.0", "parent-uuid": "SHELF-1.1.0.0", "contained-holder": ["PORT-1.1.6.5", "PORT-1.1.6.8", "PORT-1.1.6.7", "PORT-1.1.6.6"], "date": "2013-11-23T00:00:00.0Z" }, diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/connectedNetworkElementsHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/connectedNetworkElementsHandler.ts deleted file mode 100644 index e6138cc38..000000000 --- a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/connectedNetworkElementsHandler.ts +++ /dev/null @@ -1,36 +0,0 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 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'; - -export interface IConnectedNetworkElementsState extends IExternalTableState { } - -// create eleactic search material data fetch handler -const connectedNetworkElementsSearchHandler = createSearchDataHandler('network-element-connection', false, { status: "Connected" }); - -export const { - actionHandler: connectedNetworkElementsActionHandler, - createActions: createConnectedNetworkElementsActions, - createProperties: createConnectedNetworkElementsProperties, - reloadAction: connectedNetworkElementsReloadAction, - - // set value action, to change a value -} = createExternal(connectedNetworkElementsSearchHandler, appState => appState.inventory.connectedNetworkElements); diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts index 0e857ffe9..b1a0c581f 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryAppRootHandler.ts @@ -18,27 +18,23 @@ // main state handler import { combineActionHandler } from '../../../../framework/src/flux/middleware'; - // ** do not remove ** +// eslint-disable-next-line @typescript-eslint/no-unused-vars import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import { IActionHandler } from '../../../../framework/src/flux/action'; -import { IInvenroryTree, inventoryTreeHandler } from './inventoryTreeHandler'; -import { IConnectedNetworkElementsState, connectedNetworkElementsActionHandler } from './connectedNetworkElementsHandler'; import { PanelId } from '../models/panelId'; +import { IInventoryDeviceListState, inventoryDeviceListActionHandler } from './inventoryDeviceListActionHandler'; +import { IInventoryElementsState, inventoryElementsActionHandler } from './inventoryElementsHandler'; +import { IInvenroryTree, inventoryTreeHandler } from './inventoryTreeHandler'; import { currentOpenPanelHandler } from './panelHandler'; -import { inventoryElementsActionHandler, IInventoryElementsState } from './inventoryElementsHandler'; export interface IInventoryAppStateState { inventoryTree: IInvenroryTree; - connectedNetworkElements: IConnectedNetworkElementsState; // used for ne selection currentOpenPanel: PanelId; inventoryElements: IInventoryElementsState; + inventoryDeviceList: IInventoryDeviceListState; } - - - declare module '../../../../framework/src/store/applicationStore' { interface IApplicationStoreState { inventory: IInventoryAppStateState; @@ -47,9 +43,9 @@ declare module '../../../../framework/src/store/applicationStore' { const actionHandlers = { inventoryTree: inventoryTreeHandler, - connectedNetworkElements: connectedNetworkElementsActionHandler, currentOpenPanel: currentOpenPanelHandler, - inventoryElements: inventoryElementsActionHandler + inventoryElements: inventoryElementsActionHandler, + inventoryDeviceList: inventoryDeviceListActionHandler, }; export const inventoryAppRootHandler = combineActionHandler(actionHandlers); diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryDeviceListActionHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryDeviceListActionHandler.ts new file mode 100644 index 000000000..7c06cad99 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryDeviceListActionHandler.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 { AllInventoryDeviceListLoadedAction, LoadAllInventoryDeviceListAction } from '../actions/inventoryDeviceListActions'; +import { InventoryDeviceListType } from '../models/inventoryDeviceListType'; + +export interface IInventoryDeviceListState { + inventoryDeviceList: InventoryDeviceListType[]; + busy: boolean; +} + +const inventoryDeviceListListStateInit: IInventoryDeviceListState = { + inventoryDeviceList: [], + busy: false, +}; + +export const inventoryDeviceListActionHandler: IActionHandler = (state = inventoryDeviceListListStateInit, action) => { + if (action instanceof LoadAllInventoryDeviceListAction) { + + state = { + ...state, + busy: true, + }; + + } else if (action instanceof AllInventoryDeviceListLoadedAction) { + if (!action.error && action.inventoryDeviceList) { + state = { + ...state, + inventoryDeviceList: action.inventoryDeviceList, + busy: false, + }; + } else { + state = { + ...state, + busy: false, + }; + } + } + return state; +}; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.ts index a65319efa..7bac8f632 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryElementsHandler.ts @@ -15,7 +15,7 @@ * the License. * ============LICENSE_END========================================================================== */ -import { createExternal,IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; +import { createExternal, IExternalTableState } from '../../../../framework/src/components/material-table/utilities'; import { createSearchDataHandler } from '../../../../framework/src/utilities/elasticSearch'; import { InventoryType } from '../models/inventory'; @@ -23,7 +23,7 @@ import { InventoryType } from '../models/inventory'; export interface IInventoryElementsState extends IExternalTableState { } // create eleactic search material data fetch handler -const inventoryElementsSearchHandler = createSearchDataHandler("inventory"); +const inventoryElementsSearchHandler = createSearchDataHandler('inventory'); export const { actionHandler: inventoryElementsActionHandler, diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryTreeHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryTreeHandler.ts index 9029a6719..fe90d9820 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryTreeHandler.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/inventoryTreeHandler.ts @@ -16,10 +16,10 @@ * ============LICENSE_END========================================================================== */ -import { IActionHandler } from "../../../../framework/src/flux/action"; +import { IActionHandler } from '../../../../framework/src/flux/action'; -import { SetBusyAction, UpdateInventoryTreeAction, UpdateSelectedNodeAction, SetSearchTextAction, UpdateExpandedNodesAction } from "../actions/inventoryTreeActions"; -import { InventoryTreeNode, InventoryType, TreeDemoItem } from "../models/inventory"; +import { SetBusyAction, SetSearchTextAction, UpdateExpandedNodesAction, UpdateInventoryTreeAction, UpdateSelectedNodeAction } from '../actions/inventoryTreeActions'; +import { InventoryTreeNode, InventoryType, TreeDemoItem } from '../models/inventory'; export interface IInvenroryTree { @@ -33,10 +33,10 @@ export interface IInvenroryTree { const initialState: IInvenroryTree = { isBusy: false, rootNodes: [], - searchTerm: "", + searchTerm: '', selectedNode: undefined, expandedItems: [], -} +}; const getTreeDataFromInvetoryTreeNode = (node: InventoryTreeNode): TreeDemoItem[] => Object.keys(node).reduce((acc, key) => { @@ -61,8 +61,8 @@ export const inventoryTreeHandler: IActionHandler = (state = ini } else if (action instanceof UpdateSelectedNodeAction) { state = { ...state, selectedNode: action.selectedNode }; } else if (action instanceof UpdateExpandedNodesAction) { - state = { ...state, expandedItems: action.expandedNodes || []} + state = { ...state, expandedItems: action.expandedNodes || [] }; } return state; -} \ No newline at end of file +}; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/panelHandler.ts b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/panelHandler.ts index 761253112..7912d0ea5 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/handlers/panelHandler.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/handlers/panelHandler.ts @@ -1,11 +1,11 @@ -import { PanelId } from "../models/panelId"; -import { IActionHandler } from "../../../../framework/src/flux/action"; -import { SetPanelAction } from "../actions/panelActions"; +import { IActionHandler } from '../../../../framework/src/flux/action'; +import { SetPanelAction } from '../actions/panelActions'; +import { PanelId } from '../models/panelId'; export const currentOpenPanelHandler: IActionHandler = (state = null, action) => { - if (action instanceof SetPanelAction) { - state = action.panelId; - } - return state; - } \ No newline at end of file + if (action instanceof SetPanelAction) { + state = action.panelId; + } + return state; +}; \ 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 index a6c990529..a09fd7e41 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/models/inventory.ts @@ -35,7 +35,7 @@ export type InventoryType = { partTypeId: string; modelIdentifier: string; typeName: string; -} +}; export type InventoryTreeNode = { [key: string]: { @@ -44,7 +44,7 @@ export type InventoryTreeNode = { isMatch?: boolean; ownSeverity?: string; childrenSeveritySummary?: string; - } -} + }; +}; export type TreeDemoItem = ExternalTreeItem; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/models/inventoryDeviceListType.ts b/sdnr/wt/odlux/apps/inventoryApp/src/models/inventoryDeviceListType.ts new file mode 100644 index 000000000..ab2411401 --- /dev/null +++ b/sdnr/wt/odlux/apps/inventoryApp/src/models/inventoryDeviceListType.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 inventory history data. + */ + +export type InventoryDeviceListType = { + nodeId: string; +}; diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/models/networkElementConnection.ts b/sdnr/wt/odlux/apps/inventoryApp/src/models/networkElementConnection.ts index 88f70181c..e1ef1ea2d 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/models/networkElementConnection.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/models/networkElementConnection.ts @@ -24,7 +24,7 @@ export type NetworkElementConnection = { username?: string; password?: string; isRequired?: boolean; - status?: "connected" | "mounted" | "unmounted" | "connecting" | "disconnected" | "idle"; + status?: 'connected' | 'mounted' | 'unmounted' | 'connecting' | 'disconnected' | 'idle'; coreModelCapability?: string; deviceType?: string; nodeDetails?: { @@ -33,5 +33,5 @@ export type NetworkElementConnection = { failureReason: string; capability: string; }[]; - } -} + }; +}; diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/models/panelId.ts b/sdnr/wt/odlux/apps/inventoryApp/src/models/panelId.ts index b05c1db64..8f8224c8c 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/models/panelId.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/models/panelId.ts @@ -16,4 +16,4 @@ * ============LICENSE_END========================================================================== */ -export type PanelId = null | "InventoryElementsTable" | "TreeviewTable"; \ No newline at end of file +export type PanelId = null | 'Equipment' | 'TreeView'; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx index 50339c0ab..819859919 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx +++ b/sdnr/wt/odlux/apps/inventoryApp/src/pluginInventory.tsx @@ -16,38 +16,33 @@ * ============LICENSE_END========================================================================== */ // app configuration and main entry point for the app +import React from 'react'; +import { Redirect, Route, RouteComponentProps, Switch, withRouter } from 'react-router-dom'; -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 { connect, Connect, IDispatcher } from '../../../framework/src/flux/connect'; import applicationManager from '../../../framework/src/services/applicationManager'; -import connect, { Connect, IDispatcher } from '../../../framework/src/flux/connect'; -import { IApplicationStoreState } from "../../../framework/src/store/applicationStore"; - -import { InventoryTreeView } from './views/treeview'; +import { IApplicationStoreState } from '../../../framework/src/store/applicationStore'; +import { SetPanelAction } from './actions/panelActions'; +import inventoryAppRootHandler from './handlers/inventoryAppRootHandler'; +import { createInventoryElementsActions, createInventoryElementsProperties } from './handlers/inventoryElementsHandler'; +import { PanelId } from './models/panelId'; import Dashboard from './views/dashboard'; +import { InventoryTreeView } from './views/treeview'; -import { PanelId } from "./models/panelId"; -import { SetPanelAction } from "./actions/panelActions"; - -import inventoryAppRootHandler from './handlers/inventoryAppRootHandler'; -import { createInventoryElementsActions, createInventoryElementsProperties } from "./handlers/inventoryElementsHandler"; -import { createConnectedNetworkElementsProperties, createConnectedNetworkElementsActions } from "./handlers/connectedNetworkElementsHandler"; +const appIcon = require('./assets/icons/inventoryAppIcon.svg'); // select app icon let currentMountId: string | undefined = undefined; const mapProps = (state: IApplicationStoreState) => ({ inventoryProperties: createInventoryElementsProperties(state), panelId: state.inventory.currentOpenPanel, - connectedNetworkElementsProperties: createConnectedNetworkElementsProperties(state), }); -const mapDisp = (dispatcher: IDispatcher) => ({ +const mapDispatch = (dispatcher: IDispatcher) => ({ inventoryActions: createInventoryElementsActions(dispatcher.dispatch, true), - connectedNetworkElementsActions: createConnectedNetworkElementsActions(dispatcher.dispatch, true), setCurrentPanel: (panelId: PanelId) => dispatcher.dispatch(new SetPanelAction(panelId)), }); -const InventoryTableApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ mountId?: string }> & Connect) => { +const InventoryTableApplicationRouteAdapter = connect(mapProps, mapDispatch)((props: RouteComponentProps<{ mountId?: string }> & Connect) => { if (currentMountId !== props.match.params.mountId) { // route parameter has changed currentMountId = props.match.params.mountId || undefined; @@ -56,26 +51,20 @@ const InventoryTableApplicationRouteAdapter = connect(mapProps, mapDisp)((props: if (currentMountId) { if (props.panelId) { props.setCurrentPanel(props.panelId); + } else { + props.setCurrentPanel('Equipment'); } - else { - props.setCurrentPanel("InventoryElementsTable"); - } - props.inventoryActions.onFilterChanged("nodeId", currentMountId); - props.connectedNetworkElementsActions.onFilterChanged("nodeId", currentMountId); + props.inventoryActions.onFilterChanged('nodeId', currentMountId); if (!props.inventoryProperties.showFilter) { props.inventoryActions.onToggleFilter(false); } - if (!props.connectedNetworkElementsProperties.showFilter) { - props.connectedNetworkElementsActions.onToggleFilter(false); - } props.inventoryActions.onRefresh(); - props.connectedNetworkElementsActions.onRefresh(); } }); } return ( - ) + ); }); const App = withRouter((props: RouteComponentProps) => ( @@ -89,11 +78,11 @@ const App = withRouter((props: RouteComponentProps) => ( export function register() { applicationManager.registerApplication({ - name: "inventory", - icon: faShoppingBag, + name: 'inventory', + icon: appIcon, rootActionHandler: inventoryAppRootHandler, rootComponent: App, - menuEntry: "Inventory" + menuEntry: 'Inventory', }); } diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/services/inventoryService.ts b/sdnr/wt/odlux/apps/inventoryApp/src/services/inventoryService.ts index cb70bf522..4014fcf6d 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/services/inventoryService.ts +++ b/sdnr/wt/odlux/apps/inventoryApp/src/services/inventoryService.ts @@ -15,54 +15,76 @@ * the License. * ============LICENSE_END========================================================================== */ +import { Result } from '../../../../framework/src/models/elasticSearch'; import { requestRest } from '../../../../framework/src/services/restService'; -import { convertPropertyNames, replaceHyphen } from '../../../../framework/src/utilities/yangHelper'; import { InventoryTreeNode, InventoryType } from '../models/inventory'; -import { getTree, getElement } from '../fakeData'; +import { InventoryDeviceListType } from '../models/inventoryDeviceListType'; /** * Represents a web api accessor service for all maintenence entries related actions. */ class InventoryService { - public async getInventoryTree(mountId: string, searchTerm: string = ""): Promise { + public async getInventoryTree(mountId: string, searchTerm: string = ''): Promise { //return await getTree(searchTerm); const path = `/tree/read-inventoryequipment-tree/${mountId}`; const body = { - "query": searchTerm + 'query': searchTerm, }; - const inventoryTree = await requestRest(path, { method: "POST" , body: JSON.stringify(body)}); + const inventoryTree = await requestRest(path, { method: 'POST', body: JSON.stringify(body) }); return inventoryTree && inventoryTree || null; } public async getInventoryEntry(id: string): Promise { - const path = `/rests/operations/data-provider:read-inventory-list`; + const path = '/rests/operations/data-provider:read-inventory-list'; const body = { - "data-provider:input": { - "filter": [ - { property: "id", filtervalue: id }, + 'data-provider:input': { + 'filter': [ + { property: 'id', filtervalue: id }, ], - "sortorder": [], - "pagination": { - "size": 1, - "page": 1 - } - } + 'sortorder': [], + 'pagination': { + 'size': 1, + 'page': 1, + }, + }, }; const inventoryTreeElement = await requestRest<{ - "data-provider:output": { - "pagination": { - "size": number, - "page": number, - "total": number + 'data-provider:output': { + 'pagination': { + 'size': number; + 'page': number; + 'total': number; + }; + 'data': InventoryType[]; + }; + }>(path, { method: 'POST', body: JSON.stringify(body) }); + + return inventoryTreeElement && inventoryTreeElement['data-provider:output'] && inventoryTreeElement['data-provider:output'].pagination && inventoryTreeElement['data-provider:output'].pagination.total >= 1 && + inventoryTreeElement['data-provider:output'].data && inventoryTreeElement['data-provider:output'].data[0] || undefined; + // return await getElement(id); + } + + /** + * Gets all nodes from the inventory device list. + */ + public async getInventoryDeviceList(): Promise<(InventoryDeviceListType)[] | null> { + const path = '/rests/operations/data-provider:read-inventory-device-list'; + const query = { + 'data-provider:input': { + 'filter': [], + 'sortorder': [], + 'pagination': { + 'size': 20, + 'page': 1, }, - "data": InventoryType[] - } - }>(path, { method: "POST", body: JSON.stringify(body) }); + }, + }; - return inventoryTreeElement && inventoryTreeElement["data-provider:output"] && inventoryTreeElement["data-provider:output"].pagination && inventoryTreeElement["data-provider:output"].pagination.total >= 1 && - inventoryTreeElement["data-provider:output"].data && inventoryTreeElement["data-provider:output"].data[0] || undefined; - // return await getElement(id); + const result = await requestRest>(path, { method: 'POST', body: JSON.stringify(query) }); + return result && result['data-provider:output'] && result['data-provider:output'].data && result['data-provider:output'].data.map(ne => ({ + nodeId: ne, + })) || null; } } diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx index 284f70239..acd2c6216 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx +++ b/sdnr/wt/odlux/apps/inventoryApp/src/views/dashboard.tsx @@ -16,99 +16,93 @@ * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import 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 { AppBar, Tabs, Tab, MenuItem, Typography } from "@mui/material"; import Refresh from '@mui/icons-material/Refresh'; -import { PanelId } from "../models/panelId"; -import { setPanelAction } from "../actions/panelActions"; +import { AppBar, MenuItem, Tab, Tabs, Typography } from '@mui/material'; - -import { createConnectedNetworkElementsProperties, createConnectedNetworkElementsActions } from "../handlers/connectedNetworkElementsHandler"; - -import { NetworkElementConnection } from "../models/networkElementConnection"; - -import { InventoryType } from '../models/inventory'; - -import { createInventoryElementsProperties, createInventoryElementsActions } from "../handlers/inventoryElementsHandler"; import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions'; +import { ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; + +import { loadAllInventoryDeviceListAsync } from '../actions/inventoryDeviceListActions'; import { updateInventoryTreeAsyncAction } from '../actions/inventoryTreeActions'; +import { setPanelAction } from '../actions/panelActions'; import RefreshInventoryDialog, { RefreshInventoryDialogMode } from '../components/refreshInventoryDialog'; +import { createInventoryElementsActions, createInventoryElementsProperties } from '../handlers/inventoryElementsHandler'; +import { InventoryType } from '../models/inventory'; +import { InventoryDeviceListType } from '../models/inventoryDeviceListType'; +import { PanelId } from '../models/panelId'; const InventoryTable = MaterialTable as MaterialTableCtorType; const mapProps = (state: IApplicationStoreState) => ({ - connectedNetworkElementsProperties: createConnectedNetworkElementsProperties(state), panelId: state.inventory.currentOpenPanel, inventoryElementsProperties: createInventoryElementsProperties(state), - inventoryElements: state.inventory.inventoryElements + inventoryElements: state.inventory.inventoryElements, + inventoryDeviceList: state.inventory.inventoryDeviceList.inventoryDeviceList, }); const mapDispatch = (dispatcher: IDispatcher) => ({ - connectedNetworkElementsActions: createConnectedNetworkElementsActions(dispatcher.dispatch), switchActivePanel: (panelId: PanelId) => { dispatcher.dispatch(setPanelAction(panelId)); }, inventoryElementsActions: createInventoryElementsActions(dispatcher.dispatch), navigateToApplication: (applicationName: string, path?: string) => dispatcher.dispatch(new NavigateToApplication(applicationName, path)), updateInventoryTree: (mountId: string, searchTerm?: string) => dispatcher.dispatch(updateInventoryTreeAsyncAction(mountId, searchTerm)), + getAllInventoryDeviceList: async () => { + await dispatcher.dispatch(loadAllInventoryDeviceListAsync); + }, }); let treeViewInitialSorted = false; let inventoryInitialSorted = false; -const ConnectedElementTable = MaterialTable as MaterialTableCtorType; +const InventoryDeviceListTable = MaterialTable as MaterialTableCtorType; type DashboardComponentProps = RouteComponentProps & Connect; type DashboardComponentState = { - refreshInventoryEditorMode: RefreshInventoryDialogMode -} + refreshInventoryEditorMode: RefreshInventoryDialogMode; +}; class DashboardSelectorComponent extends React.Component { constructor(props: DashboardComponentProps) { super(props); this.state = { - refreshInventoryEditorMode: RefreshInventoryDialogMode.None + refreshInventoryEditorMode: RefreshInventoryDialogMode.None, }; } private onHandleTabChange = (event: React.SyntheticEvent, newValue: PanelId) => { this.onTogglePanel(newValue); - } + }; private onTogglePanel = (panelId: PanelId) => { const nextActivePanel = panelId; this.props.switchActivePanel(nextActivePanel); switch (nextActivePanel) { - case 'InventoryElementsTable': + case 'Equipment': if (!inventoryInitialSorted) { - this.props.inventoryElementsActions.onHandleExplicitRequestSort("nodeId", "asc"); + this.props.inventoryElementsActions.onHandleExplicitRequestSort('nodeId', 'asc'); inventoryInitialSorted = true; } else { this.props.inventoryElementsActions.onRefresh(); } break; - case 'TreeviewTable': - if (!treeViewInitialSorted) { - this.props.connectedNetworkElementsActions.onHandleExplicitRequestSort("nodeId", "asc"); - treeViewInitialSorted = true; - } else { - this.props.connectedNetworkElementsActions.onRefresh(); - } + case 'TreeView': + this.props.getAllInventoryDeviceList(); break; case null: // do nothing if all panels are closed break; default: - console.warn("Unknown nextActivePanel [" + nextActivePanel + "] in connectView"); + console.warn('Unknown nextActivePanel [' + nextActivePanel + '] in connectView'); break; } @@ -116,47 +110,47 @@ class DashboardSelectorComponent extends React.Component { return [ - { this.props.updateInventoryTree(rowData.nodeId, rowData.uuid); this.props.navigateToApplication("inventory", rowData.nodeId) }}>View in Treeview, + { this.props.updateInventoryTree(rowData.nodeId, rowData.uuid); this.props.navigateToApplication('inventory', rowData.nodeId); }}>View in Treeview, ]; - } + }; render() { const refreshInventoryAction = { icon: Refresh, tooltip: 'Refresh Inventory', ariaLabel: 'refresh', onClick: () => { this.setState({ - refreshInventoryEditorMode: RefreshInventoryDialogMode.RefreshInventoryTable + refreshInventoryEditorMode: RefreshInventoryDialogMode.RefreshInventoryTable, }); - } + }, }; const { panelId: activePanelId } = this.props; return ( <> - - + + { - activePanelId === "InventoryElementsTable" && + activePanelId === 'Equipment' && <> - { @@ -171,22 +165,20 @@ class DashboardSelectorComponent extends React.Component { - this.props.navigateToApplication("inventory", row.nodeId); - this.props.updateInventoryTree(row.nodeId, '*'); - }} - columns={[ - { property: "nodeId", title: "Node 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 > - + activePanelId === 'TreeView' && + <> + { + this.props.navigateToApplication('inventory', row.nodeId); + this.props.updateInventoryTree(row.nodeId, '*'); + }} + rows={this.props.inventoryDeviceList} asynchronus + columns={[ + { property: 'nodeId', title: 'Node Name', type: ColumnType.text }, + ]} idProperty="nodeId" > + + } ); @@ -194,15 +186,14 @@ class DashboardSelectorComponent extends React.Component { this.setState({ - refreshInventoryEditorMode: RefreshInventoryDialogMode.None + refreshInventoryEditorMode: RefreshInventoryDialogMode.None, }); - } - componentDidMount() { + }; + componentDidMount() { if (this.props.panelId === null) { //set default tab if none is set - this.onTogglePanel("InventoryElementsTable"); + this.onTogglePanel('Equipment'); } - } } diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/views/detail.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/views/detail.tsx index 252663935..8d47ec3d9 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/views/detail.tsx +++ b/sdnr/wt/odlux/apps/inventoryApp/src/views/detail.tsx @@ -15,20 +15,19 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from "react"; -import { withRouter, RouteComponentProps } from 'react-router-dom'; +import React from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; import Button from '@mui/material/Button'; import { Theme } from '@mui/material/styles'; // infra for styling - import { WithStyles } from '@mui/styles'; -import withStyles from '@mui/styles/withStyles'; import createStyles from '@mui/styles/createStyles'; +import withStyles from '@mui/styles/withStyles'; const styles = (theme: Theme) => createStyles({ warnButton: { - backgroundColor: theme.palette.primary.dark - } + backgroundColor: theme.palette.primary.dark, + }, }); type DetailProps = RouteComponentProps<{ id: string }> & WithStyles; @@ -37,8 +36,8 @@ export const Detail = withStyles( styles )( withRouter( (props: DetailProps) =>

    Detail {props.match.params.id}

    This are the information about {props.staticContext}.

    - - + +
    ))); diff --git a/sdnr/wt/odlux/apps/inventoryApp/src/views/treeview.tsx b/sdnr/wt/odlux/apps/inventoryApp/src/views/treeview.tsx index b0e962daa..954c074c1 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/src/views/treeview.tsx +++ b/sdnr/wt/odlux/apps/inventoryApp/src/views/treeview.tsx @@ -15,42 +15,38 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from "react"; -import { Theme } from '@mui/material/styles'; +import React from 'react'; +import Breadcrumbs from '@mui/material/Breadcrumbs'; +import Link from '@mui/material/Link'; +import { Theme } from '@mui/material/styles'; import { WithStyles } from '@mui/styles'; -import withStyles from '@mui/styles/withStyles'; import createStyles from '@mui/styles/createStyles'; - +import withStyles from '@mui/styles/withStyles'; +import { RouteComponentProps } from 'react-router-dom'; +import { SearchMode, TreeView, TreeViewCtorType } from '../../../../framework/src/components/material-ui/treeView'; import { renderObject } from '../../../../framework/src/components/objectDump'; import { Connect, connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { TreeView, TreeViewCtorType, SearchMode } from '../../../../framework/src/components/material-ui/treeView'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import { IApplicationStoreState } from "../../../../framework/src/store/applicationStore"; - -import Breadcrumbs from '@mui/material/Breadcrumbs'; -import Link from '@mui/material/Link'; - -import { updateInventoryTreeAsyncAction, selectInventoryNodeAsyncAction, UpdateSelectedNodeAction, UpdateExpandedNodesAction, setSearchTermAction } from "../actions/inventoryTreeActions"; -import { TreeDemoItem } from "../models/inventory"; - -import { RouteComponentProps } from 'react-router-dom'; +import { selectInventoryNodeAsyncAction, setSearchTermAction, UpdateExpandedNodesAction, updateInventoryTreeAsyncAction, UpdateSelectedNodeAction } from '../actions/inventoryTreeActions'; +import { TreeDemoItem } from '../models/inventory'; const styles = (theme: Theme) => createStyles({ root: { - flex: "1 0 0%", - display: "flex", - flexDirection: "row", + flex: '1 0 0%', + display: 'flex', + flexDirection: 'row', }, tree: { - flex: "1 0 0%", - minWidth: "250px", - padding: `0px ${theme.spacing(1)}` + wordWrap: 'break-word', + minWidth: '250px', + padding: `0px ${theme.spacing(1)}`, }, details: { - flex: "5 0 0%", - padding: `0px ${theme.spacing(1)}` - } + flex: '5 0 0%', + padding: `0px ${theme.spacing(1)}`, + }, }); const mapProps = (state: IApplicationStoreState) => ({ @@ -68,19 +64,19 @@ const mapDispatch = (dispatcher: IDispatcher) => ({ setSearchTerm: (searchTerm: string) => dispatcher.dispatch(setSearchTermAction(searchTerm)), }); -const propsChache = Symbol("PropsCache"); +const propsChache = Symbol('PropsCache'); const InventoryTree = TreeView as any as TreeViewCtorType; -type TreeviewComponentProps = RouteComponentProps<{ mountId: string }> & WithStyles & Connect +type TreeviewComponentProps = RouteComponentProps<{ mountId: string }> & WithStyles & Connect; type TreeviewComponentState = { [propsChache]: { rootNodes?: TreeDemoItem[]; }; rootNodes: TreeDemoItem[]; -} +}; class DashboardComponent extends React.Component { @@ -96,14 +92,15 @@ class DashboardComponent extends React.Component
    updateInventoryTree(mountId, searchTerm)} expandedItems={expendedItems} onFolderClick={(item) => { const indexOfItemToToggle = expendedItems.indexOf(item); if (indexOfItemToToggle === -1) { @@ -141,20 +139,15 @@ class DashboardComponent extends React.Component selectTreeNode(elm.value)} />
    { - selectedNode && renderObject(selectedNode, "tree-view") || null + selectedNode && renderObject(selectedNode, 'tree-view') || null }
    ); } - componentDidMount() { - const { updateInventoryTree, searchTerm, match: { params: { mountId } } } = this.props; - updateInventoryTree(mountId, searchTerm); - } - componentWillUnmount() { - this.props.setSearchTerm("*"); + this.props.setSearchTerm('*'); } } diff --git a/sdnr/wt/odlux/apps/inventoryApp/tsconfig.json b/sdnr/wt/odlux/apps/inventoryApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/inventoryApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js b/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js index 403cc53f5..6a780560d 100644 --- a/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/inventoryApp/webpack.config.js @@ -57,6 +57,16 @@ module.exports = (env) => { use: [{ loader: "babel-loader" }] + },{ + //don't minify images + test: /\.(png|gif|jpg|svg)$/, + use: [{ + loader: 'url-loader', + options: { + limit: 10, + name: './images/[name].[ext]' + } + }] }] }, @@ -127,37 +137,37 @@ module.exports = (env) => { }, proxy: { "/oauth2/": { - target: "http://localhost:8181", + target: "http://sdnc-web:8080", secure: false }, "/database/": { - target: "http://localhost:8181", + target: "http://sdnc-web:8080", secure: false }, "/restconf/": { - target: "http://localhost:8181", + target: "http://sdnc-web:8080", secure: false }, "/rests/": { - target: "http://localhost:8181", + target: "http://sdnc-web:8080", secure: false }, "/help/": { - target: "http://localhost:8181", + target: "http://sdnc-web:8080", secure: false }, "/tree/": { - target: "http://localhost:8181", + target: "http://sdnc-web:8080", secure: false }, "/websocket": { - target: "http://localhost:8181", + target: "http://sdnc-web:8080", ws: true, changeOrigin: true, secure: false }, "/yang-schema": { - target: "http://localhost:8181", + target: "http://sdnc-web:8080", ws: true, changeOrigin: true, secure: false diff --git a/sdnr/wt/odlux/apps/maintenanceApp/package.json b/sdnr/wt/odlux/apps/maintenanceApp/package.json index dd678d693..d7c325409 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/package.json +++ b/sdnr/wt/odlux/apps/maintenanceApp/package.json @@ -39,6 +39,8 @@ "jquery": "3.3.1", "react": "17.0.2", "react-dom": "17.0.2", - "react-router-dom": "5.2.0" + "react-router-dom": "5.2.0", + "@fortawesome/free-solid-svg-icons": "5.6.3", + "@fortawesome/react-fontawesome": "0.1.14" } } diff --git a/sdnr/wt/odlux/apps/maintenanceApp/pom.xml b/sdnr/wt/odlux/apps/maintenanceApp/pom.xml index f70e5fd05..b296a0aa0 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/pom.xml +++ b/sdnr/wt/odlux/apps/maintenanceApp/pom.xml @@ -19,6 +19,7 @@ ~ ============LICENSE_END======================================================= ~ --> + 4.0.0 @@ -139,7 +140,7 @@ initialize - v12.13.0 + v12.22.0 v1.22.10 diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts b/sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts index 162d94367..740abff85 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/actions/maintenenceActions.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions */ /** * ============LICENSE_START======================================================================== * ONAP : ccsdk feature sdnr wt odlux @@ -15,16 +16,13 @@ * the License. * ============LICENSE_END========================================================================== */ +import { AddSnackbarNotification } from '../../../../framework/src/actions/snackbarActions'; 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 { maintenanceEntriesReloadAction } from '../handlers/maintenanceEntriesHandler'; +import { MaintenanceEntry, spoofSymbol } from '../models/maintenanceEntryType'; import { maintenenceService } from '../services/maintenenceService'; -import { maintenanceEntriesReloadAction } from '../handlers/maintenenceEntriesHandler'; export class BaseAction extends Action { } @@ -32,7 +30,7 @@ export class LoadAllMainteneceEntriesAction extends BaseAction { } export class AllMainteneceEntriesLoadedAction extends BaseAction { - constructor (public maintenenceEntries: MaintenenceEntry[] | null, error?:string) { + constructor(public maintenenceEntries: MaintenanceEntry[] | null) { super(); } @@ -40,39 +38,39 @@ export class AllMainteneceEntriesLoadedAction extends BaseAction { export class UpdateMaintenanceEntry extends BaseAction { - constructor(public maintenenceEntry: MaintenenceEntry) { + constructor(public maintenenceEntry: MaintenanceEntry) { super(); } } /** Represents an async thunk action creator to add an element to the maintenence entries. */ -export const addOrUpdateMaintenenceEntryAsyncActionCreator = (entry: MaintenenceEntry) => (dispatch: Dispatch) => { +export const addOrUpdateMaintenenceEntryAsyncActionCreator = (entry: MaintenanceEntry) => (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' } })); + dispatch(new AddSnackbarNotification({ message: `Successfully ${result && result.created ? 'created' : 'updated'} maintenance settings for [${entry.nodeId}]`, options: { variant: 'success' } })); }, 900); - dispatch(maintenanceEntriesReloadAction) + dispatch(maintenanceEntriesReloadAction); }); }; /** Represents an async thunk action creator to delete an element from the maintenence entries. */ -export const removeFromMaintenenceEntrysAsyncActionCreator = (entry: MaintenenceEntry) => (dispatch: Dispatch) => { +export const removeFromMaintenenceEntrysAsyncActionCreator = (entry: MaintenanceEntry) => (dispatch: Dispatch) => { maintenenceService.deleteMaintenenceEntry(entry).then(result => { result && window.setTimeout(() => { dispatch(new UpdateMaintenanceEntry({ [spoofSymbol]: true, - _id: entry._id, + mId: entry.mId, nodeId: entry.nodeId, - description: "", - start: "", - end: "", - active: false + description: '', + start: '', + end: '', + active: false, })); dispatch(new AddSnackbarNotification({ message: `Successfully removed [${entry.nodeId}]`, options: { variant: 'success' } })); }, 900); - dispatch(maintenanceEntriesReloadAction) + dispatch(maintenanceEntriesReloadAction); }); }; diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/assets/icons/maintenanceAppIcon.svg b/sdnr/wt/odlux/apps/maintenanceApp/src/assets/icons/maintenanceAppIcon.svg new file mode 100644 index 000000000..8b99a5e7f --- /dev/null +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/assets/icons/maintenanceAppIcon.svg @@ -0,0 +1,50 @@ + + + + + + + + + + + diff --git a/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx b/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx index 829289224..9ab147ca7 100644 --- a/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx +++ b/sdnr/wt/odlux/apps/maintenanceApp/src/components/editMaintenenceEntryDialog.tsx @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-unused-expressions */ /** * ============LICENSE_START======================================================================== * ONAP : ccsdk feature sdnr wt odlux @@ -18,90 +19,89 @@ import * as React from 'react'; import Button from '@mui/material/Button'; -import TextField from '@mui/material/TextField'; import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogContentText from '@mui/material/DialogContentText'; import DialogTitle from '@mui/material/DialogTitle'; +import TextField from '@mui/material/TextField'; -import { IDispatcher, connect, Connect } from '../../../../framework/src/flux/connect'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; +import { FormControl, InputLabel, MenuItem, Select, Typography } from '@mui/material'; import { addOrUpdateMaintenenceEntryAsyncActionCreator, - removeFromMaintenenceEntrysAsyncActionCreator + removeFromMaintenenceEntrysAsyncActionCreator, } from '../actions/maintenenceActions'; - -import { MaintenenceEntry } from '../models/maintenenceEntryType'; -import { FormControl, InputLabel, Select, MenuItem, Typography } from '@mui/material'; +import { MaintenanceEntry } from '../models/maintenanceEntryType'; export enum EditMaintenenceEntryDialogMode { - None = "none", - AddMaintenenceEntry = "addMaintenenceEntry", - EditMaintenenceEntry = "editMaintenenceEntry", - RemoveMaintenenceEntry = "removeMaintenenceEntry" + None = 'none', + AddMaintenenceEntry = 'addMaintenenceEntry', + EditMaintenenceEntry = 'editMaintenenceEntry', + RemoveMaintenenceEntry = 'removeMaintenenceEntry', } const mapDispatch = (dispatcher: IDispatcher) => ({ - addOrUpdateMaintenenceEntry: (entry: MaintenenceEntry) => { + addOrUpdateMaintenenceEntry: (entry: MaintenanceEntry) => { dispatcher.dispatch(addOrUpdateMaintenenceEntryAsyncActionCreator(entry)); }, - removeMaintenenceEntry: (entry: MaintenenceEntry) => { + removeMaintenenceEntry: (entry: MaintenanceEntry) => { dispatcher.dispatch(removeFromMaintenenceEntrysAsyncActionCreator(entry)); }, }); type DialogSettings = { - dialogTitle: string, - dialogDescription: string, - applyButtonText: string, - cancelButtonText: string, - enableMountIdEditor: boolean, - enableTimeEditor: boolean, -} + dialogTitle: string; + dialogDescription: string; + applyButtonText: string; + cancelButtonText: string; + enableMountIdEditor: boolean; + enableTimeEditor: boolean; +}; const settings: { [key: string]: DialogSettings } = { [EditMaintenenceEntryDialogMode.None]: { - dialogTitle: "", - dialogDescription: "", - applyButtonText: "", - cancelButtonText: "", + dialogTitle: '', + dialogDescription: '', + applyButtonText: '', + cancelButtonText: '', enableMountIdEditor: false, enableTimeEditor: false, }, [EditMaintenenceEntryDialogMode.AddMaintenenceEntry]: { - dialogTitle: "Add new maintenence entry", - dialogDescription: "", - applyButtonText: "Add", - cancelButtonText: "Cancel", + 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", + dialogTitle: 'Edit maintenence entry', + dialogDescription: '', + applyButtonText: 'Save', + cancelButtonText: 'Cancel', enableMountIdEditor: false, enableTimeEditor: true, }, [EditMaintenenceEntryDialogMode.RemoveMaintenenceEntry]: { - dialogTitle: "Remove maintenence entry", - dialogDescription: "", - applyButtonText: "Remove", - cancelButtonText: "Cancel", + dialogTitle: 'Remove maintenence entry', + dialogDescription: '', + applyButtonText: 'Remove', + cancelButtonText: 'Cancel', enableMountIdEditor: false, enableTimeEditor: false, }, -} +}; type EditMaintenenceEntryDIalogComponentProps = Connect & { mode: EditMaintenenceEntryDialogMode; - initialMaintenenceEntry: MaintenenceEntry; + initialMaintenenceEntry: MaintenanceEntry; onClose: () => void; }; -type EditMaintenenceEntryDIalogComponentState = MaintenenceEntry & { isErrorVisible: boolean }; +type EditMaintenenceEntryDIalogComponentState = MaintenanceEntry & { isErrorVisible: boolean }; class EditMaintenenceEntryDIalogComponent extends React.Component { constructor(props: EditMaintenenceEntryDIalogComponentProps) { @@ -109,7 +109,7 @@ class EditMaintenenceEntryDIalogComponent extends React.Component {setting.dialogDescription} - { this.setState({ nodeId: event.target.value }); }} /> + { this.setState({ nodeId: event.target.value }); }} /> {this.state.isErrorVisible && Name must not be empty.} - { this.setState({ start: event.target.value }); }} /> - { this.setState({ end: event.target.value }); }} /> + { this.setState({ start: event.target.value }); }} /> + { this.setState({ end: event.target.value }); }} /> Active props.onFilterChanged("suspectIntervalFlag", event.target.value as string)}> - None - true - false - - - - } - - ) + const suspectIntervalFlag = props.filters.suspectIntervalFlag === undefined ? undefined : props.filters.suspectIntervalFlag.toString(); + return ( + <> + { + props.isVisible && +
    + props.onFilterChanged('radioSignalId', event.target.value)} InputLabelProps={{ + shrink: true, + }} /> + props.onFilterChanged('scannerId', event.target.value)} InputLabelProps={{ + shrink: true, + }} /> + props.onFilterChanged('timeStamp', event.target.value)} InputLabelProps={{ + shrink: true, + }} /> + + Suspect Interval -} + + + + } + + ); +}; export default ChartFilter; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx index 14cc02d6d..5f925a9ad 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/crossPolarDiscrimination.tsx @@ -15,20 +15,19 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; - -import { MaterialTable, ColumnType, MaterialTableCtorType, ColumnModel } from '../../../../framework/src/components/material-table'; +import { ColumnModel, ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { CrossPolarDiscriminationDataType, CrossPolarDiscriminationDatabaseDataType } from '../models/crossPolarDiscriminationDataType'; +import { SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions'; +import { createCrossPolarDiscriminationActions, createCrossPolarDiscriminationProperties } from '../handlers/crossPolarDiscriminationHandler'; import { IDataSet, IDataSetsObject } from '../models/chartTypes'; -import { createCrossPolarDiscriminationProperties, createCrossPolarDiscriminationActions } from '../handlers/crossPolarDiscriminationHandler'; +import { CrossPolarDiscriminationDatabaseDataType, CrossPolarDiscriminationDataType } from '../models/crossPolarDiscriminationDataType'; import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; import { addColumnLabels } from '../utils/tableUtils'; -import { SetSubViewAction, SetFilterVisibility } from '../actions/toggleActions'; import ToggleContainer from './toggleContainer'; @@ -36,18 +35,18 @@ const mapProps = (state: IApplicationStoreState) => ({ crossPolarDiscriminationProperties: createCrossPolarDiscriminationProperties(state), currentView: state.performanceHistory.subViews.CPD.subView, isFilterVisible: state.performanceHistory.subViews.CPD.isFilterVisible, - existingFilter: state.performanceHistory.crossPolarDiscrimination.filter + existingFilter: state.performanceHistory.crossPolarDiscrimination.filter, }); const mapDisp = (dispatcher: IDispatcher) => ({ crossPolarDiscriminationActions: createCrossPolarDiscriminationActions(dispatcher.dispatch), - setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("CPD", value)), - toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility("CPD", value)) }, + setSubView: (value: 'chart' | 'table') => dispatcher.dispatch(new SetSubViewAction('CPD', value)), + toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility('CPD', value));}, }); type CrossPolarDiscriminationComponentProps = RouteComponentProps & Connect & { - selectedTimePeriod: string + selectedTimePeriod: string; }; const CrossPolarDiscriminationTable = MaterialTable as MaterialTableCtorType; @@ -55,21 +54,20 @@ const CrossPolarDiscriminationTable = MaterialTable as MaterialTableCtorType{ - +class CrossPolarDiscriminationComponent extends React.Component { onToggleFilterButton = () => { this.props.toggleFilterButton(!this.props.isFilterVisible); - } + }; - onChange = (value: "chart" | "table") => { + onChange = (value: 'chart' | 'table') => { this.props.setSubView(value); - } + }; onFilterChanged = (property: string, filterTerm: string) => { this.props.crossPolarDiscriminationActions.onFilterChanged(property, filterTerm); if (!this.props.crossPolarDiscriminationProperties.showFilter) this.props.crossPolarDiscriminationActions.onToggleFilter(false); - } + }; render(): JSX.Element { const properties = this.props.crossPolarDiscriminationProperties; @@ -78,12 +76,12 @@ class CrossPolarDiscriminationComponent extends React.Component[] = [ - { 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: '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", type: ColumnType.boolean - } + property: 'suspectIntervalFlag', title: 'Suspect Interval', type: ColumnType.boolean, + }, ]; chartPagedData.datasets.forEach(ds => { @@ -91,66 +89,67 @@ class CrossPolarDiscriminationComponent extends React.Component - + {lineChart(chartPagedData)} - + ); - }; + } /** * 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 data_rows = [...rows]; + sortDataByTimeStamp(data_rows); const datasets: IDataSet[] = [{ - name: "xpdMin", - label: "xpd-min", + name: 'xpdMin', + label: 'xpd-min', borderColor: '#0e17f3de', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "CPD (min)[db]" + columnLabel: 'CPD (min)[db]', }, { - name: "xpdAvg", - label: "xpd-avg", + name: 'xpdAvg', + label: 'xpd-avg', borderColor: '#08edb6de', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "CPD (avg)[db]" + columnLabel: 'CPD (avg)[db]', }, { - name: "xpdMax", - label: "xpd-max", + name: 'xpdMax', + label: 'xpd-max', borderColor: '#b308edde', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "CPD (max)[db]" + columnLabel: 'CPD (max)[db]', }]; - _rows.forEach(row => { + data_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 + x: row['timeStamp' as keyof CrossPolarDiscriminationDataType] as string, + y: row.performanceData[ds.name as keyof CrossPolarDiscriminationDatabaseDataType] as string, }); }); }); return { - datasets: datasets + datasets: datasets, }; - } + }; } const CrossPolarDiscrimination = withRouter(connect(mapProps, mapDisp)(CrossPolarDiscriminationComponent)); export default CrossPolarDiscrimination; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/ltpSelection.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/ltpSelection.tsx index ef6cfc712..bd6333be7 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/ltpSelection.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/ltpSelection.tsx @@ -15,90 +15,91 @@ * the License. * ============LICENSE_END========================================================================== */ +import React from 'react'; -import * as React from 'react'; -import { MenuItem, Select, FormControl, Typography, SelectChangeEvent } from '@mui/material'; +import { FormControl, MenuItem, Select, SelectChangeEvent, Typography } from '@mui/material'; +import { Theme } from '@mui/material/styles'; import makeStyles from '@mui/styles/makeStyles'; -import { LtpIds } from 'models/availableLtps'; import { Loader } from '../../../../framework/src/components/material-ui'; - -import { Theme } from '@mui/material/styles'; +import { LtpIds } from '../models/availableLtps'; const useStyles = makeStyles((theme: Theme) => ({ - display: { - display: "inline-block" - }, - selectDropdown: { - borderRadius: 1, - position: "relative", - backgroundColor: theme.palette.background.paper, - border: "1px solid #ced4da", - fontSize: 16, - width: "auto", - padding: "5px 5px 5px 5px", - transition: theme.transitions.create(["border-color", "box-shadow"]), - }, - center: { - "flex": "1", - "height": "100%", - "display": "flex", - "alignItems": "center", - "justifyContent": "center", - flexDirection: "column" - } + display: { + display: 'inline-block', + }, + selectDropdown: { + borderRadius: 1, + position: 'relative', + backgroundColor: theme.palette.background.paper, + border: '1px solid #ced4da', + fontSize: 16, + width: 'auto', + padding: '5px 5px 5px 5px', + transition: theme.transitions.create(['border-color', 'box-shadow']), + }, + center: { + 'flex': '1', + 'height': '100%', + 'display': 'flex', + 'alignItems': 'center', + 'justifyContent': 'center', + flexDirection: 'column', + }, })); -type LtpSelectionProps = { selectedNE: string, error?: string, finishedLoading: boolean, selectedLtp: string, - availableLtps: LtpIds[], - onChangeLtp(event: SelectChangeEvent ): void, - selectedTimePeriod: string, - onChangeTimePeriod(event: SelectChangeEvent ): void }; +type LtpSelectionProps = { + selectedNE: string; error?: string; finishedLoading: boolean; selectedLtp: string; + availableLtps: LtpIds[]; + onChangeLtp(event: SelectChangeEvent): void; + selectedTimePeriod: string; + onChangeTimePeriod(event: SelectChangeEvent): void; +}; export const LtpSelection = (props: LtpSelectionProps) => { - const classes = useStyles(); - return ( - <> -

    Selected Network Element: {props.selectedNE}

    - - - Select LTP - - - Time-Period - - - { - !props.finishedLoading && !props.error && -
    - -

    Collecting Data ...

    -
    - } - { - props.finishedLoading && props.error && -
    -

    Data couldn't be loaded

    - {props.error} -
    - } - { - props.selectedLtp === "-1" && props.finishedLoading && !props.error && (props.availableLtps.length > 0 ? -
    -

    Please select a LTP

    -
    - : -
    -

    No performance data found

    -
    ) - } - ) -} + const classes = useStyles(); + return ( + <> +

    Selected Network Element: {props.selectedNE}

    + + + Select LTP + + + Time-Period + + + { + !props.finishedLoading && !props.error && +
    + +

    Collecting Data ...

    +
    + } + { + props.finishedLoading && props.error && +
    +

    Data couldn't be loaded

    + {props.error} +
    + } + { + props.selectedLtp === '-1' && props.finishedLoading && !props.error && (props.availableLtps.length > 0 ? +
    +

    Please select a LTP

    +
    + : +
    +

    No performance data found

    +
    ) + } + ); +}; export default LtpSelection; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx index 6a06ea351..fb608aac6 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/performanceData.tsx @@ -15,36 +15,36 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; - -import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { ColumnModel, ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { PerformanceDataType, PerformanceDatabaseDataType } from '../models/performanceDataType'; + +import { SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions'; +import { createPerformanceDataActions, createPerformanceDataProperties } from '../handlers/performanceDataHandler'; import { IDataSet, IDataSetsObject } from '../models/chartTypes'; -import { createPerformanceDataProperties, createPerformanceDataActions } from '../handlers/performanceDataHandler'; +import { PerformanceDatabaseDataType, PerformanceDataType } from '../models/performanceDataType'; import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; import { addColumnLabels } from '../utils/tableUtils'; import ToggleContainer from './toggleContainer'; -import { SetSubViewAction, SetFilterVisibility } from '../actions/toggleActions'; const mapProps = (state: IApplicationStoreState) => ({ performanceDataProperties: createPerformanceDataProperties(state), currentView: state.performanceHistory.subViews.performanceData.subView, isFilterVisible: state.performanceHistory.subViews.performanceData.isFilterVisible, - existingFilter: state.performanceHistory.performanceData.filter + existingFilter: state.performanceHistory.performanceData.filter, }); const mapDisp = (dispatcher: IDispatcher) => ({ performanceDataActions: createPerformanceDataActions(dispatcher.dispatch), - setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("performanceData", value)), - toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility("performanceData", value)) } + setSubView: (value: 'chart' | 'table') => dispatcher.dispatch(new SetSubViewAction('performanceData', value)), + toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility('performanceData', value)); }, }); type PerformanceDataComponentProps = RouteComponentProps & Connect & { - selectedTimePeriod: string + selectedTimePeriod: string; }; const PerformanceDataTable = MaterialTable as MaterialTableCtorType; @@ -52,17 +52,16 @@ const PerformanceDataTable = MaterialTable as MaterialTableCtorType{ - +class PerformanceDataComponent extends React.Component { onToggleFilterButton = () => { this.props.toggleFilterButton(!this.props.isFilterVisible); - } + }; onFilterChanged = (property: string, filterTerm: string) => { this.props.performanceDataActions.onFilterChanged(property, filterTerm); if (!this.props.performanceDataProperties.showFilter) this.props.performanceDataActions.onToggleFilter(false); - } + }; render(): JSX.Element { const properties = this.props.performanceDataProperties; @@ -70,12 +69,12 @@ class PerformanceDataComponent extends React.Component[] = [ - { 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: '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", type: ColumnType.boolean - } + property: 'suspectIntervalFlag', title: 'Suspect Interval', type: ColumnType.boolean, + }, ]; chartPagedData.datasets.forEach(ds => { @@ -83,67 +82,68 @@ class PerformanceDataComponent extends React.Component - this.props.toggleFilterButton(!this.props.isFilterVisible)} existingFilter={this.props.existingFilter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} showFilter={this.props.isFilterVisible} onChange={this.props.setSubView}> + this.props.toggleFilterButton(!this.props.isFilterVisible)} + existingFilter={this.props.existingFilter} onFilterChanged={this.onFilterChanged} selectedValue={this.props.currentView} showFilter={this.props.isFilterVisible} onChange={this.props.setSubView}> {lineChart(chartPagedData)} - + ); - }; + } /** * 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 data_rows = [...rows]; + sortDataByTimeStamp(data_rows); const datasets: IDataSet[] = [{ - name: "es", - label: "es", + name: 'es', + label: 'es', borderColor: '#0e17f3de', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "ES" + columnLabel: 'ES', }, { - name: "ses", - label: "ses", + name: 'ses', + label: 'ses', borderColor: '#08edb6de', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "SES" + columnLabel: 'SES', }, { - name: "unavailability", - label: "unavailability", + name: 'unavailability', + label: 'unavailability', borderColor: '#b308edde', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "Unavailability" + columnLabel: 'Unavailability', }]; - _rows.forEach(row => { + data_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 + x: row['timeStamp' as keyof PerformanceDataType] as string, + y: row.performanceData[ds.name as keyof PerformanceDatabaseDataType] as string, }); }); }); return { - datasets: datasets + datasets: datasets, }; - } + }; } const PerformanceData = withRouter(connect(mapProps, mapDisp)(PerformanceDataComponent)); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx index 8dc92b8ac..125cc6ee6 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/receiveLevel.tsx @@ -15,37 +15,36 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; - -import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { ColumnModel, ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { ReceiveLevelDataType, ReceiveLevelDatabaseDataType } from '../models/receiveLevelDataType'; +import { SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions'; +import { createReceiveLevelActions, createReceiveLevelProperties } from '../handlers/receiveLevelHandler'; import { IDataSet, IDataSetsObject } from '../models/chartTypes'; -import { createReceiveLevelProperties, createReceiveLevelActions } from '../handlers/receiveLevelHandler'; +import { ReceiveLevelDatabaseDataType, ReceiveLevelDataType } from '../models/receiveLevelDataType'; import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; import { addColumnLabels } from '../utils/tableUtils'; -import { SetSubViewAction, SetFilterVisibility } from '../actions/toggleActions'; import ToggleContainer from './toggleContainer'; const mapProps = (state: IApplicationStoreState) => ({ receiveLevelProperties: createReceiveLevelProperties(state), currentView: state.performanceHistory.subViews.receiveLevel.subView, isFilterVisible: state.performanceHistory.subViews.receiveLevel.isFilterVisible, - existingFilter: state.performanceHistory.receiveLevel.filter + existingFilter: state.performanceHistory.receiveLevel.filter, }); const mapDisp = (dispatcher: IDispatcher) => ({ receiveLevelActions: createReceiveLevelActions(dispatcher.dispatch), - setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("receiveLevel", value)), - toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility("receiveLevel", value)) }, + setSubView: (value: 'chart' | 'table') => dispatcher.dispatch(new SetSubViewAction('receiveLevel', value)), + toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility('receiveLevel', value)); }, }); type ReceiveLevelComponentProps = RouteComponentProps & Connect & { - selectedTimePeriod: string + selectedTimePeriod: string; }; const ReceiveLevelTable = MaterialTable as MaterialTableCtorType; @@ -53,22 +52,21 @@ const ReceiveLevelTable = MaterialTable as MaterialTableCtorType{ - +class ReceiveLevelComponent extends React.Component { onToggleFilterButton = () => { this.props.toggleFilterButton(!this.props.isFilterVisible); - } + }; - onChange = (value: "chart" | "table") => { + onChange = (value: 'chart' | 'table') => { this.props.setSubView(value); - } + }; onFilterChanged = (property: string, filterTerm: string) => { this.props.receiveLevelActions.onFilterChanged(property, filterTerm); if (!this.props.receiveLevelProperties.showFilter) this.props.receiveLevelActions.onToggleFilter(false); - } + }; render(): JSX.Element { const properties = this.props.receiveLevelProperties; @@ -76,12 +74,12 @@ class ReceiveLevelComponent extends React.Component{ const chartPagedData = this.getChartDataValues(properties.rows); const receiveLevelColumns: ColumnModel[] = [ - { 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: '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", type: ColumnType.boolean - } + property: 'suspectIntervalFlag', title: 'Suspect Interval', type: ColumnType.boolean, + }, ]; chartPagedData.datasets.forEach(ds => { @@ -92,64 +90,64 @@ class ReceiveLevelComponent extends React.Component{ <> {lineChart(chartPagedData)} - + ); - }; + } /** * 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 data_rows = [...rows]; + sortDataByTimeStamp(data_rows); const datasets: IDataSet[] = [{ - name: "rxLevelMin", - label: "rx-level-min", + name: 'rxLevelMin', + label: 'rx-level-min', borderColor: '#0e17f3de', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "Rx min" + columnLabel: 'Rx min', }, { - name: "rxLevelAvg", - label: "rx-level-avg", + name: 'rxLevelAvg', + label: 'rx-level-avg', borderColor: '#08edb6de', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "Rx avg" + columnLabel: 'Rx avg', }, { - name: "rxLevelMax", - label: "rx-level-max", + name: 'rxLevelMax', + label: 'rx-level-max', borderColor: '#b308edde', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "Rx max" + columnLabel: 'Rx max', }]; - _rows.forEach(row => { + data_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 + x: row['timeStamp' as keyof ReceiveLevelDataType] as string, + y: row.performanceData[ds.name as keyof ReceiveLevelDatabaseDataType] as string, }); }); }); return { - datasets: datasets + datasets: datasets, }; - } + }; } const ReceiveLevel = withRouter(connect(mapProps, mapDisp)(ReceiveLevelComponent)); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx index ee7fe34aa..85241fbb1 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/signalToInterference.tsx @@ -15,37 +15,36 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; - -import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { ColumnModel, ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { SignalToInterferenceDataType, SignalToInterferenceDatabaseDataType } from '../models/signalToInteferenceDataType'; +import { SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions'; +import { createSignalToInterferenceActions, createSignalToInterferenceProperties } from '../handlers/signalToInterferenceHandler'; import { IDataSet, IDataSetsObject } from '../models/chartTypes'; -import { createSignalToInterferenceProperties, createSignalToInterferenceActions } from '../handlers/signalToInterferenceHandler'; +import { SignalToInterferenceDatabaseDataType, SignalToInterferenceDataType } from '../models/signalToInteferenceDataType'; import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; import { addColumnLabels } from '../utils/tableUtils'; -import { SetSubViewAction, SetFilterVisibility } from '../actions/toggleActions'; import ToggleContainer from './toggleContainer'; const mapProps = (state: IApplicationStoreState) => ({ signalToInterferenceProperties: createSignalToInterferenceProperties(state), currentView: state.performanceHistory.subViews.SINR.subView, isFilterVisible: state.performanceHistory.subViews.SINR.isFilterVisible, - existingFilter: state.performanceHistory.signalToInterference.filter + existingFilter: state.performanceHistory.signalToInterference.filter, }); const mapDisp = (dispatcher: IDispatcher) => ({ signalToInterferenceActions: createSignalToInterferenceActions(dispatcher.dispatch), - setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("SINR", value)), - toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility("SINR", value)) }, + setSubView: (value: 'chart' | 'table') => dispatcher.dispatch(new SetSubViewAction('SINR', value)), + toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility('SINR', value)); }, }); type SignalToInterferenceComponentProps = RouteComponentProps & Connect & { - selectedTimePeriod: string + selectedTimePeriod: string; }; const SignalToInterferenceTable = MaterialTable as MaterialTableCtorType; @@ -53,21 +52,20 @@ const SignalToInterferenceTable = MaterialTable as MaterialTableCtorType{ - +class SignalToInterferenceComponent extends React.Component { onToggleFilterButton = () => { this.props.toggleFilterButton(!this.props.isFilterVisible); - } + }; - onChange = (value: "chart" | "table") => { + onChange = (value: 'chart' | 'table') => { this.props.setSubView(value); - } + }; onFilterChanged = (property: string, filterTerm: string) => { this.props.signalToInterferenceActions.onFilterChanged(property, filterTerm); if (!this.props.signalToInterferenceProperties.showFilter) this.props.signalToInterferenceActions.onToggleFilter(false); - } + }; render(): JSX.Element { const properties = this.props.signalToInterferenceProperties; @@ -76,12 +74,12 @@ class SignalToInterferenceComponent extends React.Component[] = [ - { 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: '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", type: ColumnType.boolean - } + property: 'suspectIntervalFlag', title: 'Suspect Interval', type: ColumnType.boolean, + }, ]; chartPagedData.datasets.forEach(ds => { @@ -89,14 +87,15 @@ class SignalToInterferenceComponent extends React.Component - + {lineChart(chartPagedData)} - ); - }; + } /** * This function gets the performance values for SINR according on the chartjs dataset structure @@ -104,53 +103,53 @@ class SignalToInterferenceComponent extends React.Component { - const _rows = [...rows]; - sortDataByTimeStamp(_rows); + const data_rows = [...rows]; + sortDataByTimeStamp(data_rows); const datasets: IDataSet[] = [{ - name: "snirMin", - label: "snir-min", + name: 'snirMin', + label: 'snir-min', borderColor: '#0e17f3de', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "SINR (min)[db]" + columnLabel: 'SINR (min)[db]', }, { - name: "snirAvg", - label: "snir-avg", + name: 'snirAvg', + label: 'snir-avg', borderColor: '#08edb6de', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "SINR (avg)[db]" + columnLabel: 'SINR (avg)[db]', }, { - name: "snirMax", - label: "snir-max", + name: 'snirMax', + label: 'snir-max', borderColor: '#b308edde', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "SINR (max)[db]" + columnLabel: 'SINR (max)[db]', }]; - _rows.forEach(row => { + data_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 + x: row['timeStamp' as keyof SignalToInterferenceDataType] as string, + y: row.performanceData[ds.name as keyof SignalToInterferenceDatabaseDataType] as string, }); }); }); return { - datasets: datasets + datasets: datasets, }; - } + }; } const SignalToInterference = withRouter(connect(mapProps, mapDisp)(SignalToInterferenceComponent)); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx index 31e1d363f..d4b823387 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/temperature.tsx @@ -15,38 +15,37 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; - -import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { ColumnModel, ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { TemperatureDataType, TemperatureDatabaseDataType } from '../models/temperatureDataType'; +import { SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions'; +import { createTemperatureActions, createTemperatureProperties } from '../handlers/temperatureHandler'; import { IDataSet, IDataSetsObject } from '../models/chartTypes'; -import { createTemperatureProperties, createTemperatureActions } from '../handlers/temperatureHandler'; +import { TemperatureDatabaseDataType, TemperatureDataType } from '../models/temperatureDataType'; import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; import { addColumnLabels } from '../utils/tableUtils'; import ToggleContainer from './toggleContainer'; -import { SetSubViewAction, SetFilterVisibility } from '../actions/toggleActions'; const mapProps = (state: IApplicationStoreState) => ({ temperatureProperties: createTemperatureProperties(state), currentView: state.performanceHistory.subViews.temperatur.subView, isFilterVisible: state.performanceHistory.subViews.temperatur.isFilterVisible, - existingFilter: state.performanceHistory.temperature.filter + existingFilter: state.performanceHistory.temperature.filter, }); const mapDisp = (dispatcher: IDispatcher) => ({ temperatureActions: createTemperatureActions(dispatcher.dispatch), - setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("Temp", value)), - toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility("Temp", value)) }, + setSubView: (value: 'chart' | 'table') => dispatcher.dispatch(new SetSubViewAction('Temp', value)), + toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility('Temp', value)); }, }); type TemperatureComponentProps = RouteComponentProps & Connect & { - selectedTimePeriod: string + selectedTimePeriod: string; }; const TemperatureTable = MaterialTable as MaterialTableCtorType; @@ -54,22 +53,21 @@ const TemperatureTable = MaterialTable as MaterialTableCtorType{ - +class TemperatureComponent extends React.Component { onToggleFilterButton = () => { this.props.toggleFilterButton(!this.props.isFilterVisible); - } + }; - onChange = (value: "chart" | "table") => { + onChange = (value: 'chart' | 'table') => { this.props.setSubView(value); - } + }; onFilterChanged = (property: string, filterTerm: string) => { this.props.temperatureActions.onFilterChanged(property, filterTerm); if (!this.props.temperatureProperties.showFilter) this.props.temperatureActions.onToggleFilter(false); - } + }; render(): JSX.Element { const properties = this.props.temperatureProperties; @@ -77,12 +75,12 @@ class TemperatureComponent extends React.Component{ const chartPagedData = this.getChartDataValues(properties.rows); const temperatureColumns: ColumnModel[] = [ - { 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: '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", type: ColumnType.boolean - } + property: 'suspectIntervalFlag', title: 'Suspect Interval', type: ColumnType.boolean, + }, ]; chartPagedData.datasets.forEach(ds => { @@ -93,11 +91,11 @@ class TemperatureComponent extends React.Component{ {lineChart(chartPagedData)} - + ); - }; + } /** * This function gets the performance values for Temperature according on the chartjs dataset structure @@ -105,53 +103,53 @@ class TemperatureComponent extends React.Component{ */ private getChartDataValues = (rows: TemperatureDataType[]): IDataSetsObject => { - const _rows = [...rows]; - sortDataByTimeStamp(_rows); + const data_rows = [...rows]; + sortDataByTimeStamp(data_rows); const datasets: IDataSet[] = [{ - name: "rfTempMin", - label: "rf-temp-min", + name: 'rfTempMin', + label: 'rf-temp-min', borderColor: '#0e17f3de', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "Rf Temp Min[deg C]" + columnLabel: 'Rf Temp Min[deg C]', }, { - name: "rfTempAvg", - label: "rf-temp-avg", + name: 'rfTempAvg', + label: 'rf-temp-avg', borderColor: '#08edb6de', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "Rf Temp Avg[deg C]" + columnLabel: 'Rf Temp Avg[deg C]', }, { - name: "rfTempMax", - label: "rf-temp-max", + name: 'rfTempMax', + label: 'rf-temp-max', borderColor: '#b308edde', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "Rf Temp Max[deg C]" + columnLabel: 'Rf Temp Max[deg C]', }]; - _rows.forEach(row => { + data_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 + x: row['timeStamp' as keyof TemperatureDataType] as string, + y: row.performanceData[ds.name as keyof TemperatureDatabaseDataType] as string, }); }); }); return { - datasets: datasets + datasets: datasets, }; - } + }; } const Temperature = withRouter(connect(mapProps, mapDisp)(TemperatureComponent)); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/toggleContainer.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/toggleContainer.tsx index 8696fe4d6..e883aef7f 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/toggleContainer.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/toggleContainer.tsx @@ -17,52 +17,54 @@ */ import * as React from 'react'; -import ToggleButton from '@mui/material/ToggleButton'; -import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; + import BarChartIcon from '@mui/icons-material/BarChart'; +import FilterListIcon from '@mui/icons-material/FilterList'; import TableChartIcon from '@mui/icons-material/TableChart'; -import makeStyles from '@mui/styles/makeStyles'; +import ToggleButton from '@mui/material/ToggleButton'; +import ToggleButtonGroup from '@mui/material/ToggleButtonGroup'; import Tooltip from '@mui/material/Tooltip'; -import ChartFilter from './chartFilter' -import FilterListIcon from '@mui/icons-material/FilterList'; +import makeStyles from '@mui/styles/makeStyles'; + +import ChartFilter from './chartFilter'; const styles = makeStyles({ - toggleButtonContainer: { - display: "flex", - alignItems: "center", - justifyContent: "center", - padding: "10px", - }, - subViewGroup: { - padding: "10px" - }, - filterGroup: { - marginLeft: "10px" - } + toggleButtonContainer: { + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + padding: '10px', + }, + subViewGroup: { + padding: '10px', + }, + filterGroup: { + marginLeft: '10px', + }, }); -type toggleProps = { selectedValue: string, onChange(value: string): void, showFilter: boolean, onToggleFilterButton(): void, onFilterChanged: (property: string, filterTerm: string) => void, existingFilter: any }; +type toggleProps = { selectedValue: string; onChange(value: string): void; showFilter: boolean; onToggleFilterButton(): void; onFilterChanged: (property: string, filterTerm: string) => void; existingFilter: any }; const ToggleContainer: React.FunctionComponent = (props) => { - const classes = styles(); + const classes = styles(); - const handleChange = (event: React.MouseEvent, newView: string) => { - if (newView !== null) { - props.onChange(newView) - } - }; + const handleChange = (event: React.MouseEvent, newView: string) => { + if (newView !== null) { + props.onChange(newView); + } + }; - const handleFilterChange = (event: React.MouseEvent, newView: string) => { - props.onToggleFilterButton(); - }; + const handleFilterChange = (_event: React.MouseEvent) => { + props.onToggleFilterButton(); + }; - const children = React.Children.toArray(props.children); + const children = React.Children.toArray(props.children); - //hide filter if visible + table - //put current name into state, let container handle stuff itelf, register for togglestate, get right via set name + //hide filter if visible + table + //put current name into state, let container handle stuff itelf, register for togglestate, get right via set name - return ( + return ( <>
    @@ -79,7 +81,7 @@ const ToggleContainer: React.FunctionComponent = (props) => { - + @@ -89,13 +91,12 @@ const ToggleContainer: React.FunctionComponent = (props) => {
    { - props.selectedValue === "chart" && + props.selectedValue === 'chart' && } - {props.selectedValue === "chart" ? children[0] : props.selectedValue === "table" && children[1]} + {props.selectedValue === 'chart' ? children[0] : props.selectedValue === 'table' && children[1]} ); - -} +}; export default ToggleContainer; \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx index 97a35da0a..db9a7c077 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/components/transmissionPower.tsx @@ -15,75 +15,73 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; +import { RouteComponentProps, withRouter } from 'react-router-dom'; -import { withRouter, RouteComponentProps } from 'react-router-dom'; - -import { MaterialTable, ColumnType, ColumnModel, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { ColumnModel, ColumnType, MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { TransmissionPowerDataType, TransmissionPowerDatabaseDataType } from '../models/transmissionPowerDataType'; +import { SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions'; +import { createTransmissionPowerActions, createTransmissionPowerProperties } from '../handlers/transmissionPowerHandler'; import { IDataSet, IDataSetsObject } from '../models/chartTypes'; -import { createTransmissionPowerProperties, createTransmissionPowerActions } from '../handlers/transmissionPowerHandler'; +import { TransmissionPowerDatabaseDataType, TransmissionPowerDataType } from '../models/transmissionPowerDataType'; import { lineChart, sortDataByTimeStamp } from '../utils/chartUtils'; import { addColumnLabels } from '../utils/tableUtils'; -import { SetSubViewAction, SetFilterVisibility } from '../actions/toggleActions'; import ToggleContainer from './toggleContainer'; const mapProps = (state: IApplicationStoreState) => ({ transmissionPowerProperties: createTransmissionPowerProperties(state), currentView: state.performanceHistory.subViews.transmissionPower.subView, isFilterVisible: state.performanceHistory.subViews.transmissionPower.isFilterVisible, - existingFilter: state.performanceHistory.transmissionPower.filter + existingFilter: state.performanceHistory.transmissionPower.filter, }); const mapDisp = (dispatcher: IDispatcher) => ({ transmissionPowerActions: createTransmissionPowerActions(dispatcher.dispatch), - setSubView: (value: "chart" | "table") => dispatcher.dispatch(new SetSubViewAction("transmissionPower", value)), - toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility("transmissionPower", value)) }, + setSubView: (value: 'chart' | 'table') => dispatcher.dispatch(new SetSubViewAction('transmissionPower', value)), + toggleFilterButton: (value: boolean) => { dispatcher.dispatch(new SetFilterVisibility('transmissionPower', value)); }, }); type TransmissionPowerComponentProps = RouteComponentProps & Connect & { - selectedTimePeriod: string -} + selectedTimePeriod: string; +}; const TransmissionPowerTable = MaterialTable as MaterialTableCtorType; /** * The Component which gets the transmission power data from the database based on the selected time period. */ -class TransmissionPowerComponent extends React.Component{ - +class TransmissionPowerComponent extends React.Component { onToggleFilterButton = () => { this.props.toggleFilterButton(!this.props.isFilterVisible); - } + }; - onChange = (value: "chart" | "table") => { + onChange = (value: 'chart' | 'table') => { this.props.setSubView(value); - } + }; onFilterChanged = (property: string, filterTerm: string) => { this.props.transmissionPowerActions.onFilterChanged(property, filterTerm); if (!this.props.transmissionPowerProperties.showFilter) this.props.transmissionPowerActions.onToggleFilter(false); - } + }; render(): JSX.Element { - const properties = this.props.transmissionPowerProperties - const actions = this.props.transmissionPowerActions + const properties = this.props.transmissionPowerProperties; + const actions = this.props.transmissionPowerActions; const chartPagedData = this.getChartDataValues(properties.rows); const transmissionColumns: ColumnModel[] = [ - { 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: '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", type: ColumnType.boolean - } + property: 'suspectIntervalFlag', title: 'Suspect Interval', type: ColumnType.boolean, + }, ]; chartPagedData.datasets.forEach(ds => { @@ -92,13 +90,14 @@ class TransmissionPowerComponent extends React.Component - + {lineChart(chartPagedData)} - + ); - }; + } /** * This function gets the performance values for TransmissionPower according on the chartjs dataset structure @@ -106,53 +105,53 @@ class TransmissionPowerComponent extends React.Component { - const _rows = [...rows]; - sortDataByTimeStamp(_rows); + const data_rows = [...rows]; + sortDataByTimeStamp(data_rows); const datasets: IDataSet[] = [{ - name: "txLevelMin", - label: "tx-level-min", + name: 'txLevelMin', + label: 'tx-level-min', borderColor: '#0e17f3de', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "Tx min" + columnLabel: 'Tx min', }, { - name: "txLevelAvg", - label: "tx-level-avg", + name: 'txLevelAvg', + label: 'tx-level-avg', borderColor: '#08edb6de', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "Tx avg" + columnLabel: 'Tx avg', }, { - name: "txLevelMax", - label: "tx-level-max", + name: 'txLevelMax', + label: 'tx-level-max', borderColor: '#b308edde', bezierCurve: false, lineTension: 0, fill: false, data: [], - columnLabel: "Tx max" + columnLabel: 'Tx max', }]; - _rows.forEach(row => { + data_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 + x: row['timeStamp' as keyof TransmissionPowerDataType] as string, + y: row.performanceData[ds.name as keyof TransmissionPowerDatabaseDataType] as string, }); }); }); return { - datasets: datasets + datasets: datasets, }; - } + }; } const TransmissionPower = withRouter(connect(mapProps, mapDisp)(TransmissionPowerComponent)); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/adaptiveModulationHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/adaptiveModulationHandler.ts index 8857845d2..9baf54529 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/adaptiveModulationHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/adaptiveModulationHandler.ts @@ -26,12 +26,12 @@ export interface IAdaptiveModulationState extends IExternalTableState(getFilter, false, null) +const adaptiveModulationSearchHandler = createSearchDataHandler(getFilter, false, null); export const { - actionHandler: adaptiveModulationActionHandler, - createActions: createAdaptiveModulationActions, - createProperties: createAdaptiveModulationProperties, - createPreActions: createAdaptiveModulationPreActions, - reloadAction: adaptiveModulationReloadAction, + actionHandler: adaptiveModulationActionHandler, + createActions: createAdaptiveModulationActions, + createProperties: createAdaptiveModulationProperties, + createPreActions: createAdaptiveModulationPreActions, + reloadAction: adaptiveModulationReloadAction, } = createExternal(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 index 4605efdb0..f943ef44c 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/availableLtpsActionHandler.ts @@ -38,7 +38,7 @@ const ltpListStateInit: IAvailableLtpsState = { distinctLtps: [], busy: false, loadedOnce: false, - error: undefined + error: undefined, }; export const availableLtpsActionHandler: IActionHandler = (state = ltpListStateInit, action) => { @@ -46,7 +46,7 @@ export const availableLtpsActionHandler: IActionHandler = ( state = { ...state, - busy: true + busy: true, }; } else if (action instanceof AllAvailableLtpsLoadedAction) { @@ -56,21 +56,21 @@ export const availableLtpsActionHandler: IActionHandler = ( distinctLtps: action.availableLtps, busy: false, error: undefined, - loadedOnce: true + loadedOnce: true, }; } else if (action.error) { state = { ...state, busy: false, loadedOnce: true, - error: action.error - } + error: action.error, + }; } } else if (action instanceof SetInitialLoadedAction) { state = { ...state, - loadedOnce: action.initialLoaded + loadedOnce: action.initialLoaded, }; } else if (action instanceof NoLtpsFoundAction) { state = { @@ -78,22 +78,20 @@ export const availableLtpsActionHandler: IActionHandler = ( busy: false, error: undefined, loadedOnce: true, - distinctLtps: [] - } + distinctLtps: [], + }; } else if (action instanceof ResetLtpsAction) { state = { ...state, busy: false, error: undefined, loadedOnce: false, - distinctLtps: [] - } - } - - else { + distinctLtps: [], + }; + } else { state = { ...state, - busy: false + busy: false, }; } diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/crossPolarDiscriminationHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/crossPolarDiscriminationHandler.ts index 5008dd56d..96cabfa83 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/crossPolarDiscriminationHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/crossPolarDiscriminationHandler.ts @@ -26,13 +26,13 @@ export interface ICrossPolarDiscriminationState extends IExternalTableState(getFilter, false, null) +const crossPolarDiscriminationSearchHandler = createSearchDataHandler(getFilter, false, null); export const { - actionHandler: crossPolarDiscriminationActionHandler, - createActions: createCrossPolarDiscriminationActions, - createProperties: createCrossPolarDiscriminationProperties, - createPreActions: createCrossPolarDiscriminationPreActions, - reloadAction: crossPolarDiscriminationReloadAction, + actionHandler: crossPolarDiscriminationActionHandler, + createActions: createCrossPolarDiscriminationActions, + createProperties: createCrossPolarDiscriminationProperties, + createPreActions: createCrossPolarDiscriminationPreActions, + reloadAction: crossPolarDiscriminationReloadAction, } = createExternal(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 index b3c0db446..11e380ad5 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/deviceListActionHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/deviceListActionHandler.ts @@ -27,7 +27,7 @@ export interface IDeviceListState { const deviceListStateInit: IDeviceListState = { deviceList: [], - busy: false + busy: false, }; export const deviceListActionHandler: IActionHandler = (state = deviceListStateInit, action) => { @@ -35,7 +35,7 @@ export const deviceListActionHandler: IActionHandler = (state state = { ...state, - busy: true + busy: true, }; } else if (action instanceof AllDeviceListLoadedAction) { @@ -43,12 +43,12 @@ export const deviceListActionHandler: IActionHandler = (state state = { ...state, deviceList: action.deviceList, - busy: false + busy: false, }; } else { state = { ...state, - busy: false + busy: false, }; } } diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceDataHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceDataHandler.ts index a7b63a324..664c7cd6c 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceDataHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceDataHandler.ts @@ -15,12 +15,11 @@ * 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 { PerformanceDataType } from '../models/performanceDataType'; import { getFilter } from '../utils/tableUtils'; export interface IPerformanceDataState extends IExternalTableState { } @@ -31,10 +30,10 @@ export interface IPerformanceDataState extends IExternalTableState(getFilter, false, null); export const { - actionHandler: performanceDataActionHandler, - createActions: createPerformanceDataActions, - createProperties: createPerformanceDataProperties, - createPreActions: createPerformanceDataPreActions, - reloadAction: performanceDataReloadAction + actionHandler: performanceDataActionHandler, + createActions: createPerformanceDataActions, + createProperties: createPerformanceDataProperties, + createPreActions: createPerformanceDataPreActions, + reloadAction: performanceDataReloadAction, } = createExternal(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 index a05ff3787..c0cee46ba 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/performanceHistoryRootHandler.ts @@ -17,29 +17,29 @@ */ // main state handler +import { IActionHandler } from '../../../../framework/src/flux/action'; import { combineActionHandler } from '../../../../framework/src/flux/middleware'; - // ** do not remove ** +// eslint-disable-next-line @typescript-eslint/no-unused-vars import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import { IActionHandler } from '../../../../framework/src/flux/action'; import { IConnectAppStoreState } from '../../../connectApp/src/handlers/connectAppRootHandler'; +import { UpdateMountId } from '../actions/deviceListActions'; +import { SetPanelAction } from '../actions/panelChangeActions'; +import { ReloadAction } from '../actions/reloadAction'; +import { TimeChangeAction } from '../actions/timeChangeAction'; +import { ResetAllSubViewsAction, SetFilterVisibility, SetSubViewAction } from '../actions/toggleActions'; +import { PmDataInterval } from '../models/performanceDataType'; +import { currentViewType, SubTabType } from '../models/toggleDataType'; +import { adaptiveModulationActionHandler, IAdaptiveModulationState } from './adaptiveModulationHandler'; +import { availableLtpsActionHandler, IAvailableLtpsState } from './availableLtpsActionHandler'; +import { crossPolarDiscriminationActionHandler, ICrossPolarDiscriminationState } from './crossPolarDiscriminationHandler'; +import { deviceListActionHandler, IDeviceListState } from './deviceListActionHandler'; 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'; -import { SetSubViewAction, ResetAllSubViewsAction, SetFilterVisibility } from '../actions/toggleActions'; -import { SubTabType, currentViewType } from '../models/toggleDataType'; -import { ReloadAction } from '../actions/reloadAction'; +import { ITemperatureState, temperatureActionHandler } from './temperatureHandler'; +import { ITransmissionPowerState, transmissionPowerActionHandler } from './transmissionPowerHandler'; export interface IPerformanceHistoryStoreState { nodeId: string; @@ -58,15 +58,15 @@ export interface IPerformanceHistoryStoreState { isReloadSchedueled: boolean; } -const mountIdHandler: IActionHandler = (state = "", action) => { +const mountIdHandler: IActionHandler = (state = '', action) => { if (action instanceof UpdateMountId) { - state = ""; + state = ''; if (action.nodeId) { state = action.nodeId; } } return state; -} +}; const reloadHandler: IActionHandler = (state = false, action) => { @@ -74,7 +74,7 @@ const reloadHandler: IActionHandler = (state = false, action) => { state = action.show; } return state; -} +}; const currentOpenPanelHandler: IActionHandler = (state = null, action) => { @@ -82,59 +82,75 @@ const currentOpenPanelHandler: IActionHandler = (state = null, ac state = action.panelId; } return state; -} +}; const currentPMDataIntervalHandler: IActionHandler = (state = PmDataInterval.pmInterval15Min, action) => { if (action instanceof TimeChangeAction) { state = action.time; } return state; -} +}; -type filterableSubview = { subView: SubTabType, isFilterVisible: boolean }; -type toggleViewDataType = { currentSubView: currentViewType, performanceData: filterableSubview, receiveLevel: filterableSubview, transmissionPower: filterableSubview, adaptiveModulation: filterableSubview, temperatur: filterableSubview, SINR: filterableSubview, CPD: filterableSubview }; +type filterableSubview = { subView: SubTabType; isFilterVisible: boolean }; +type toggleViewDataType = { + currentSubView: currentViewType; + performanceData: filterableSubview; + receiveLevel: filterableSubview; + transmissionPower: filterableSubview; + adaptiveModulation: filterableSubview; + temperatur: filterableSubview; + SINR: filterableSubview; + CPD: filterableSubview; +}; -const toogleViewDataHandler: IActionHandler = (state = { currentSubView: "performanceData", performanceData: { subView: "chart", isFilterVisible: true }, receiveLevel: { subView: "chart", isFilterVisible: true }, adaptiveModulation: { subView: "chart", isFilterVisible: true }, transmissionPower: { subView: "chart", isFilterVisible: true }, temperatur: { subView: "chart", isFilterVisible: true }, SINR: { subView: "chart", isFilterVisible: true }, CPD: { subView: "chart", isFilterVisible: true } }, action) => { +const toogleViewDataHandler: IActionHandler = ( + state = { + currentSubView: 'performanceData', + performanceData: { subView: 'chart', isFilterVisible: true }, + receiveLevel: { subView: 'chart', isFilterVisible: true }, + adaptiveModulation: { subView: 'chart', isFilterVisible: true }, + transmissionPower: { subView: 'chart', isFilterVisible: true }, + temperatur: { subView: 'chart', isFilterVisible: true }, + SINR: { subView: 'chart', isFilterVisible: true }, + CPD: { subView: 'chart', isFilterVisible: true }, + }, action) => { if (action instanceof SetSubViewAction) { switch (action.currentView) { - case "performanceData": state = { ...state, performanceData: { ...state.performanceData, subView: action.selectedTab } }; break; - case "adaptiveModulation": state = { ...state, adaptiveModulation: { ...state.adaptiveModulation, subView: action.selectedTab } }; break; - case "receiveLevel": state = { ...state, receiveLevel: { ...state.receiveLevel, subView: action.selectedTab } }; break; - case "transmissionPower": state = { ...state, transmissionPower: { ...state.transmissionPower, subView: action.selectedTab } }; break; - case "Temp": state = { ...state, temperatur: { ...state.temperatur, subView: action.selectedTab } }; break; - case "SINR": state = { ...state, SINR: { ...state.SINR, subView: action.selectedTab } }; break; - case "CPD": state = { ...state, CPD: { ...state.CPD, subView: action.selectedTab } }; break; + case 'performanceData': state = { ...state, performanceData: { ...state.performanceData, subView: action.selectedTab } }; break; + case 'adaptiveModulation': state = { ...state, adaptiveModulation: { ...state.adaptiveModulation, subView: action.selectedTab } }; break; + case 'receiveLevel': state = { ...state, receiveLevel: { ...state.receiveLevel, subView: action.selectedTab } }; break; + case 'transmissionPower': state = { ...state, transmissionPower: { ...state.transmissionPower, subView: action.selectedTab } }; break; + case 'Temp': state = { ...state, temperatur: { ...state.temperatur, subView: action.selectedTab } }; break; + case 'SINR': state = { ...state, SINR: { ...state.SINR, subView: action.selectedTab } }; break; + case 'CPD': state = { ...state, CPD: { ...state.CPD, subView: action.selectedTab } }; break; } - } - else if (action instanceof SetFilterVisibility) { + } else if (action instanceof SetFilterVisibility) { switch (action.currentView) { - case "performanceData": state = { - ...state, performanceData: { ...state.performanceData, isFilterVisible: action.isVisible } + case 'performanceData': state = { + ...state, performanceData: { ...state.performanceData, isFilterVisible: action.isVisible }, }; break; - case "adaptiveModulation": state = { ...state, adaptiveModulation: { ...state.performanceData, isFilterVisible: action.isVisible } }; break; - case "receiveLevel": state = { ...state, receiveLevel: { ...state.receiveLevel, isFilterVisible: action.isVisible } }; break; - case "transmissionPower": state = { ...state, transmissionPower: { ...state.transmissionPower, isFilterVisible: action.isVisible } }; break; - case "Temp": state = { ...state, temperatur: { ...state.temperatur, isFilterVisible: action.isVisible } }; break; - case "SINR": state = { ...state, SINR: { ...state.SINR, isFilterVisible: action.isVisible } }; break; - case "CPD": state = { ...state, CPD: { ...state.CPD, isFilterVisible: action.isVisible } }; break; + case 'adaptiveModulation': state = { ...state, adaptiveModulation: { ...state.performanceData, isFilterVisible: action.isVisible } }; break; + case 'receiveLevel': state = { ...state, receiveLevel: { ...state.receiveLevel, isFilterVisible: action.isVisible } }; break; + case 'transmissionPower': state = { ...state, transmissionPower: { ...state.transmissionPower, isFilterVisible: action.isVisible } }; break; + case 'Temp': state = { ...state, temperatur: { ...state.temperatur, isFilterVisible: action.isVisible } }; break; + case 'SINR': state = { ...state, SINR: { ...state.SINR, isFilterVisible: action.isVisible } }; break; + case 'CPD': state = { ...state, CPD: { ...state.CPD, isFilterVisible: action.isVisible } }; break; } - } else if (action instanceof ResetAllSubViewsAction) { state = { - ...state, performanceData: { ...state.performanceData, subView: "chart" }, - adaptiveModulation: { ...state.adaptiveModulation, subView: "chart" }, - receiveLevel: { ...state.receiveLevel, subView: "chart" }, - transmissionPower: { ...state.transmissionPower, subView: "chart" }, - temperatur: { ...state.temperatur, subView: "chart" }, - SINR: { ...state.SINR, subView: "chart" }, - CPD: { ...state.CPD, subView: "chart" } - } + ...state, performanceData: { ...state.performanceData, subView: 'chart' }, + adaptiveModulation: { ...state.adaptiveModulation, subView: 'chart' }, + receiveLevel: { ...state.receiveLevel, subView: 'chart' }, + transmissionPower: { ...state.transmissionPower, subView: 'chart' }, + temperatur: { ...state.temperatur, subView: 'chart' }, + SINR: { ...state.SINR, subView: 'chart' }, + CPD: { ...state.CPD, subView: 'chart' }, + }; } - return state; -} +}; declare module '../../../../framework/src/store/applicationStore' { interface IApplicationStoreState { @@ -157,7 +173,7 @@ const actionHandlers = { currentOpenPanel: currentOpenPanelHandler, pmDataIntervalType: currentPMDataIntervalHandler, subViews: toogleViewDataHandler, - isReloadSchedueled: reloadHandler + isReloadSchedueled: reloadHandler, }; const performanceHistoryRootHandler = combineActionHandler(actionHandlers); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/receiveLevelHandler.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/receiveLevelHandler.ts index 34fd3ebce..78626ebec 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/receiveLevelHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/receiveLevelHandler.ts @@ -29,10 +29,10 @@ export interface IReceiveLevelState extends IExternalTableState(getFilter, false, null); export const { - actionHandler: receiveLevelActionHandler, - createActions: createReceiveLevelActions, - createProperties: createReceiveLevelProperties, - createPreActions: createReceiveLevelPreActions, - reloadAction: receiveLevelReloadAction, + actionHandler: receiveLevelActionHandler, + createActions: createReceiveLevelActions, + createProperties: createReceiveLevelProperties, + createPreActions: createReceiveLevelPreActions, + reloadAction: receiveLevelReloadAction, } = createExternal(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 index 5fd5c0cde..ab6efabae 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/signalToInterferenceHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/signalToInterferenceHandler.ts @@ -29,10 +29,10 @@ export interface ISignalToInterferenceState extends IExternalTableState(getFilter, false, null); export const { - actionHandler: signalToInterferenceActionHandler, - createActions: createSignalToInterferenceActions, - createProperties: createSignalToInterferenceProperties, - createPreActions: createSignalToInterferencePreActions, - reloadAction: signalToInterferenceReloadAction, + actionHandler: signalToInterferenceActionHandler, + createActions: createSignalToInterferenceActions, + createProperties: createSignalToInterferenceProperties, + createPreActions: createSignalToInterferencePreActions, + reloadAction: signalToInterferenceReloadAction, } = createExternal(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 index 130b81801..02bf69be7 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperatureHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/temperatureHandler.ts @@ -26,13 +26,13 @@ export interface ITemperatureState extends IExternalTableState(getFilter, false, null); +const temperatureSearchHandler = createSearchDataHandler(getFilter, false, null); export const { - actionHandler: temperatureActionHandler, - createActions: createTemperatureActions, - createProperties: createTemperatureProperties, - createPreActions: createTemperaturePreActions, - reloadAction: temperatureReloadAction, + actionHandler: temperatureActionHandler, + createActions: createTemperatureActions, + createProperties: createTemperatureProperties, + createPreActions: createTemperaturePreActions, + reloadAction: temperatureReloadAction, } = createExternal(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 index 2a09e58f3..9cf70dc87 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPowerHandler.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/handlers/transmissionPowerHandler.ts @@ -26,13 +26,13 @@ export interface ITransmissionPowerState extends IExternalTableState(getFilter, false, null) +const transmissionPowerSearchHandler = createSearchDataHandler(getFilter, false, null); export const { - actionHandler: transmissionPowerActionHandler, - createActions: createTransmissionPowerActions, - createProperties: createTransmissionPowerProperties, - createPreActions: createTransmissionPowerPreActions, - reloadAction: transmissionPowerReloadAction, + actionHandler: transmissionPowerActionHandler, + createActions: createTransmissionPowerActions, + createProperties: createTransmissionPowerProperties, + createPreActions: createTransmissionPowerPreActions, + reloadAction: transmissionPowerReloadAction, } = createExternal(transmissionPowerSearchHandler, appState => appState.performanceHistory.transmissionPower); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts index dc6c7bc16..60061571c 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/availableLtps.ts @@ -15,7 +15,7 @@ * the License. * ============LICENSE_END========================================================================== */ - export type LtpIds = { - key : string - } +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 index 53039faa2..969c0b399 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/chartTypes.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/chartTypes.ts @@ -24,21 +24,21 @@ export interface IData { * Structure of chartjs dataset with the chart properties. */ export interface IDataSet { - name: string, - label: string, - lineTension: 0, + name: string; + label: string; + lineTension: 0; bezierCurve: boolean; - fill: boolean, - borderColor: string, - data: IData[], - columnLabel: string + fill: boolean; + borderColor: string; + data: IData[]; + columnLabel: string; } /** * Structure of chartjs dataset which is sent to the chart. */ export interface IDataSetsObject { - datasets: IDataSet[] + datasets: IDataSet[]; } /** diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts index 8adb16f45..749624b9a 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/crossPolarDiscriminationDataType.ts @@ -32,7 +32,7 @@ export type CrossPolarDiscriminationDatabaseDataType = { * Internally used type to provide table and chart data */ export type CrossPolarDiscriminationDataType = { - performanceData: CrossPolarDiscriminationDatabaseDataType + performanceData: CrossPolarDiscriminationDatabaseDataType; radioSignalId: string; scannerId: string; timeStamp: string; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/deviceListType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/deviceListType.ts index db8f2d71e..fbe314162 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/deviceListType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/deviceListType.ts @@ -22,4 +22,4 @@ 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 index 5889a64c8..08bf7f815 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/panelId.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/panelId.ts @@ -19,4 +19,4 @@ /** * Represents PanelIds for the available Expansional panels. */ -export type PanelId = null | "PerformanceData" | "ReceiveLevel" | "TransmissionPower" | "AdaptiveModulation" | "Temperature" | "SINR" | "CPD"; \ No newline at end of file +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 index 30f97fb54..f71e09de9 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/performanceDataType.ts @@ -15,7 +15,6 @@ * the License. * ============LICENSE_END========================================================================== */ -import { Moment } from "moment"; //export { HitEntry, Result } from '../../../../framework/src/models'; @@ -50,5 +49,5 @@ export type PerformanceDataType = { */ export const enum PmDataInterval { pmInterval15Min, - pmInterval24Hours + pmInterval24Hours, } \ No newline at end of file diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts index 3b0cb7683..5798d5c5d 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/temperatureDataType.ts @@ -32,7 +32,7 @@ export type TemperatureDatabaseDataType = { * Internally used type to provide table and chart data */ export type TemperatureDataType = { - performanceData: TemperatureDatabaseDataType + performanceData: TemperatureDatabaseDataType; radioSignalId: string; scannerId: string; timeStamp: string; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/toggleDataType.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/toggleDataType.ts index f705e10b2..0e71c9474 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/toggleDataType.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/toggleDataType.ts @@ -19,7 +19,7 @@ /** * Specifies possible sub views */ -export type SubTabType = "chart" | "table"; +export type SubTabType = 'chart' | 'table'; -export type currentViewType = "performanceData" | "receiveLevel" | "transmissionPower" | "adaptiveModulation" | "Temp" | "SINR" | "CPD"; +export type currentViewType = 'performanceData' | 'receiveLevel' | 'transmissionPower' | 'adaptiveModulation' | 'Temp' | 'SINR' | 'CPD'; diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/topologyNetconf.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/topologyNetconf.ts index 99123f55e..f52af9784 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/topologyNetconf.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/models/topologyNetconf.ts @@ -17,10 +17,10 @@ */ export interface TopologyNode { - "node-id": string; + 'node-id': string; } export interface Topology { - "topology-id": string; + 'topology-id': string; node: TopologyNode[]; } diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/pluginPerformance.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/pluginPerformance.tsx index a8aaca22f..ef939fdba 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/pluginPerformance.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/pluginPerformance.tsx @@ -16,45 +16,64 @@ * ============LICENSE_END========================================================================== */ -import * as React from "react"; -import { faBook } from '@fortawesome/free-solid-svg-icons'; +import React from 'react'; +import { Redirect, Route, RouteComponentProps, Switch, withRouter } from 'react-router-dom'; +import { connect, Connect, IDispatcher } from '../../../framework/src/flux/connect'; import applicationManager from '../../../framework/src/services/applicationManager'; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import { IApplicationStoreState } from '../../../framework/src/store/applicationStore'; +import { ApplicationStore } from '../../../framework/src/store/applicationStore'; -import { withRouter, RouteComponentProps, Route, Switch, Redirect } from 'react-router-dom'; +import { updateMountIdActionCreator } from './actions/deviceListActions'; +import { ResetLtpsAction } from './actions/ltpAction'; +import { ReloadAction } from './actions/reloadAction'; +import { ResetAllSubViewsAction } from './actions/toggleActions'; 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"; -import { ResetAllSubViewsAction } from "./actions/toggleActions"; -import { ResetLtpsAction } from "./actions/ltpAction"; -import { ReloadAction } from "./actions/reloadAction"; +const appIcon = require('./assets/icons/performanceHistoryAppIcon.svg'); // select app icon let api: { readonly applicationStore: ApplicationStore | null; readonly applicationStoreInitialized: Promise; -} +}; -const mapProps = (state: IApplicationStoreState) => ({ +const mapProps = () => ({ }); const mapDisp = (dispatcher: IDispatcher) => ({ updateMountId: (mountId: string) => dispatcher.dispatch(updateMountIdActionCreator(mountId)), resetLtps: () => dispatcher.dispatch(new ResetLtpsAction()), resetSubViews: () => dispatcher.dispatch(new ResetAllSubViewsAction()), - setScheduleReload: (show: boolean) => dispatcher.dispatch(new ReloadAction(show)) + setScheduleReload: (show: boolean) => dispatcher.dispatch(new ReloadAction(show)), }); let currentMountId: string | null = null; -let lastUrl: string = "/performanceHistory"; +let lastUrl: string = '/performanceHistory'; const PerformanceHistoryApplicationRouteAdapter = connect(mapProps, mapDisp)((props: RouteComponentProps<{ mountId?: string }> & Connect) => { - let mountId: string = ""; + let mountId: string = ''; + + const getMountId = (last_url: string) => { + let index = last_url.lastIndexOf('performanceHistory/'); + if (index >= 0) { + mountId = last_url.substring(index + 19); + } else { + mountId = ''; + } - // called when component finshed mounting + return mountId; + }; + + const scheduleReload = (current_mount_id: string) => { + props.updateMountId(current_mount_id); + props.resetLtps(); + props.resetSubViews(); + props.setScheduleReload(true); + }; + + // called when component finished mounting React.useEffect(() => { lastUrl = props.location.pathname; @@ -62,11 +81,11 @@ const PerformanceHistoryApplicationRouteAdapter = connect(mapProps, mapDisp)((pr if (currentMountId !== mountId) { // new element is loaded currentMountId = mountId; - schedueleReload(currentMountId); + scheduleReload(currentMountId); } else - if (currentMountId !== "") { // same element is loaded again - schedueleReload(currentMountId); - } + if (currentMountId !== '') { // same element is loaded again + scheduleReload(currentMountId); + } }, []); // called when component gets updated @@ -77,52 +96,34 @@ const PerformanceHistoryApplicationRouteAdapter = connect(mapProps, mapDisp)((pr if (currentMountId !== mountId) { currentMountId = mountId; - schedueleReload(currentMountId); + scheduleReload(currentMountId); } }); - const getMountId = (lastUrl: string) => { - let index = lastUrl.lastIndexOf("performanceHistory/"); - if (index >= 0) { - mountId = lastUrl.substr(index + 19); - } else { - mountId = ""; - } - - return mountId; - } - - const schedueleReload = (currentMountId: string) => { - props.updateMountId(currentMountId); - props.resetLtps(); - props.resetSubViews(); - props.setScheduleReload(true); - } - return ( ); }); const PerformanceHistoryRouterApp = withRouter((props: RouteComponentProps) => { - props.history.action = "POP"; + props.history.action = 'POP'; return ( - ) + ); }); export function register() { api = applicationManager.registerApplication({ - name: "performanceHistory", - icon: faBook, + name: 'performanceHistory', + icon: appIcon, rootComponent: PerformanceHistoryRouterApp, rootActionHandler: performanceHistoryRootHandler, - menuEntry: "Performance" + menuEntry: 'Performance', }); } diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts b/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts index 70a8771b7..ef013f1cb 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/services/performanceHistoryService.ts @@ -15,13 +15,12 @@ * the License. * ============LICENSE_END========================================================================== */ -import { requestRest } from '../../../../framework/src/services/restService'; import { Result } from '../../../../framework/src/models/elasticSearch'; +import { requestRest } from '../../../../framework/src/services/restService'; 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. @@ -34,26 +33,26 @@ class PerformanceService { public async getDistinctLtpsFromDatabase(networkElement: string, selectedTimePeriod: string): Promise { let path; const query = { - "filter": [{ - "property": "node-name", - "filtervalue": networkElement + 'filter': [{ + 'property': 'node-name', + 'filtervalue': networkElement, }], - "sortorder": [], - "pagination": { - "size": 20, - "page": 1 - } - } + 'sortorder': [], + 'pagination': { + 'size': 20, + 'page': 1, + }, + }; - if (selectedTimePeriod === "15min") { + if (selectedTimePeriod === '15min') { path = '/rests/operations/data-provider:read-pmdata-15m-ltp-list'; } else { path = '/rests/operations/data-provider:read-pmdata-24h-ltp-list'; } - const result = await requestRest>(path, { method: "POST", body: JSON.stringify(convertPropertyNames({ input: query }, replaceUpperCase)) }); - return result && result["data-provider:output"] && result["data-provider:output"].data && result["data-provider:output"].data.map(ne => ({ key: ne })) || null; + const result = await requestRest>(path, { method: 'POST', body: JSON.stringify(convertPropertyNames({ input: query }, replaceUpperCase)) }); + return result && result['data-provider:output'] && result['data-provider:output'].data && result['data-provider:output'].data.map(ne => ({ key: ne })) || null; } @@ -64,19 +63,19 @@ class PerformanceService { public async getDeviceListfromPerf15minHistory(): Promise<(DeviceListType)[] | null> { const path = '/rests/operations/data-provider:read-pmdata-15m-device-list'; const query = { - "data-provider:input": { - "filter": [], - "sortorder": [], - "pagination": { - "size": 20, - "page": 1 - } - } + 'data-provider:input': { + 'filter': [], + 'sortorder': [], + 'pagination': { + 'size': 20, + 'page': 1, + }, + }, }; - const result = await requestRest>(path, { method: "POST", body: JSON.stringify(query) }); - return result && result["data-provider:output"] && result["data-provider:output"].data && result["data-provider:output"].data.map(ne => ({ - nodeId: ne + const result = await requestRest>(path, { method: 'POST', body: JSON.stringify(query) }); + return result && result['data-provider:output'] && result['data-provider:output'].data && result['data-provider:output'].data.map(ne => ({ + nodeId: ne, })) || null; } @@ -86,19 +85,19 @@ class PerformanceService { public async getDeviceListfromPerf24hHistory(): Promise<(DeviceListType)[] | null> { const path = '/rests/operations/data-provider:read-pmdata-24h-device-list'; const query = { - "data-provider:input": { - "filter": [], - "sortorder": [], - "pagination": { - "size": 20, - "page": 1 - } - } + 'data-provider:input': { + 'filter': [], + 'sortorder': [], + 'pagination': { + 'size': 20, + 'page': 1, + }, + }, }; - const result = await requestRest>(path, { method: "POST", body: JSON.stringify(query) }); - return result && result["data-provider:output"] && result["data-provider:output"].data && result["data-provider:output"].data.map(ne => ({ - nodeId: ne + const result = await requestRest>(path, { method: 'POST', body: JSON.stringify(query) }); + return result && result['data-provider:output'] && result['data-provider:output'].data && result['data-provider:output'].data.map(ne => ({ + nodeId: ne, })) || null; } } diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx index f58638e30..38abb3e79 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/chartUtils.tsx @@ -15,15 +15,17 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; -import { IDataSetsObject } from '../models/chartTypes'; +import React from 'react'; +import moment from 'moment'; import { Line } from 'react-chartjs-2'; -import * as moment from 'moment'; -import { ITimeStamp } from 'models/chartTypes'; + +import { IDataSetsObject } from '../models/chartTypes'; +import { ITimeStamp } from '../models/chartTypes'; const style: React.CSSProperties = { - height: "80%" -} + height: '80%', +}; + export const lineChart = (chartPagedData: IDataSetsObject) => { return (
    @@ -44,32 +46,32 @@ export const lineChart = (chartPagedData: IDataSetsObject) => { 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' - } + labelString: 'Timestamp', + }, }], yAxes: [{ ticks: { - beginAtZero: true + beginAtZero: true, }, scaleLabel: { display: true, - labelString: 'Value' - } - }] - } + labelString: 'Value', + }, + }], + }, }} />
    ); -} +}; export const sortDataByTimeStamp = (_rows: T[]): T[] => { return (_rows.sort((a, b) => { - const result = Date.parse(a["timeStamp"]) - Date.parse(b["timeStamp"]); + const result = Date.parse(a.timeStamp) - Date.parse(b.timeStamp); return isNaN(result) ? 0 : result; })); -} \ No newline at end of file +}; \ 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 index b5a3a3f36..37fe962dc 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/tableUtils.ts +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/utils/tableUtils.ts @@ -15,22 +15,22 @@ * the License. * ============LICENSE_END========================================================================== */ -import { ColumnType, ColumnModel } from '../../../../framework/src/components/material-table'; +import { ColumnModel, ColumnType } from '../../../../framework/src/components/material-table'; import { PmDataInterval } from '../models/performanceDataType'; import { getPmDataInterval } from '../pluginPerformance'; export const addColumnLabels = (name: string, title: string, disableFilter = true, disableSorting = true): ColumnModel => { 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"); + 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 index b33b442d3..a4b968622 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/src/views/performanceHistoryApplication.tsx @@ -15,57 +15,54 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; +import { connect, Connect, IDispatcher } from '../../../../framework/src/flux/connect'; import { Theme } from '@mui/material/styles'; import { WithStyles } from '@mui/styles'; import createStyles from '@mui/styles/createStyles'; import withStyles from '@mui/styles/withStyles'; -import FormControl from '@mui/material/FormControl'; -import MenuItem from '@mui/material/MenuItem'; -import Select from '@mui/material/Select'; -import connect, { Connect, IDispatcher } from '../../../../framework/src/flux/connect'; -import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -import { Panel, Loader } from '../../../../framework/src/components/material-ui'; + import { NavigateToApplication } from '../../../../framework/src/actions/navigationActions'; import { Dispatch } from '../../../../framework/src/flux/store'; +import { IApplicationStoreState } from '../../../../framework/src/store/applicationStore'; -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, ResetLtpsAction } 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 { TimeChangeAction } from '../actions/timeChangeAction'; +import AdaptiveModulation from '../components/adaptiveModulation'; +import CrossPolarDiscrimination from '../components/crossPolarDiscrimination'; +import PerformanceData from '../components/performanceData'; +import ReceiveLevel from '../components/receiveLevel'; +import SignalToInterference from '../components/signalToInterference'; +import Temperature from '../components/temperature'; +import TransmissionPower from '../components/transmissionPower'; +import { adaptiveModulationReloadAction, createAdaptiveModulationActions, createAdaptiveModulationPreActions } from '../handlers/adaptiveModulationHandler'; +import { createCrossPolarDiscriminationActions, createCrossPolarDiscriminationPreActions, crossPolarDiscriminationReloadAction } from '../handlers/crossPolarDiscriminationHandler'; +import { createPerformanceDataActions, createPerformanceDataPreActions, performanceDataReloadAction } from '../handlers/performanceDataHandler'; +import { createReceiveLevelActions, createReceiveLevelPreActions, receiveLevelReloadAction } from '../handlers/receiveLevelHandler'; +import { createSignalToInterferenceActions, createSignalToInterferencePreActions, signalToInterferenceReloadAction } from '../handlers/signalToInterferenceHandler'; +import { createTemperatureActions, createTemperaturePreActions, temperatureReloadAction } from '../handlers/temperatureHandler'; +import { createTransmissionPowerActions, createTransmissionPowerPreActions, transmissionPowerReloadAction } from '../handlers/transmissionPowerHandler'; +import { PanelId } from '../models/panelId'; +import { PmDataInterval } from '../models/performanceDataType'; +import { AppBar, SelectChangeEvent, Tab, Tabs } from '@mui/material'; import { MaterialTable, MaterialTableCtorType } from '../../../../framework/src/components/material-table'; -import { AppBar, Tabs, Tab, SelectChangeEvent } from '@mui/material'; -import LtpSelection from '../components/ltpSelection'; -import { ResetAllSubViewsAction } from '../actions/toggleActions'; import { ReloadAction } from '../actions/reloadAction'; +import { ResetAllSubViewsAction } from '../actions/toggleActions'; +import LtpSelection from '../components/ltpSelection'; const PerformanceHistoryComponentStyles = (theme: Theme) => createStyles({ root: { - display: "flex", - flexWrap: "wrap", + display: 'flex', + flexWrap: 'wrap', }, margin: { margin: theme.spacing(1), - } + }, }); const mapProps = (state: IApplicationStoreState) => ({ @@ -75,7 +72,7 @@ const mapProps = (state: IApplicationStoreState) => ({ networkElements: state.performanceHistory.networkElements.deviceList, initialLoaded: state.performanceHistory.ltps.loadedOnce, error: state.performanceHistory.ltps.error, - shouldReload: state.performanceHistory.isReloadSchedueled + shouldReload: state.performanceHistory.isReloadSchedueled, }); const mapDispatcher = (dispatcher: IDispatcher) => ({ @@ -103,112 +100,117 @@ const mapDispatcher = (dispatcher: IDispatcher) => ({ 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)), + 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)); + dispatch(new NavigateToApplication('performanceHistory', nodeId)); }), resetLtps: () => dispatcher.dispatch((dispatch: Dispatch) => { dispatch(new ResetLtpsAction()); }), resetSubViews: () => dispatcher.dispatch(new ResetAllSubViewsAction()), - setShouldReload: (show: boolean) => dispatcher.dispatch(new ReloadAction(show)) + setShouldReload: (show: boolean) => dispatcher.dispatch(new ReloadAction(show)), }); export type NetworkElementType = { - nodeId: string, -} + nodeId: string; +}; + const NetworkElementTable = MaterialTable as MaterialTableCtorType; type PerformanceHistoryComponentProps = Connect & WithStyles; type PerformanceHistoryComponentState = { - selectedNetworkElement: string, - selectedTimePeriod: string, - selectedLtp: string, - showNetworkElementsTable: boolean, - showLtps: boolean, - showPanels: boolean, + selectedNetworkElement: string; + selectedTimePeriod: string; + selectedLtp: string; + showNetworkElementsTable: boolean; + showLtps: boolean; + showPanels: boolean; preFilter: { - "node-name": string, - "uuid-interface": string - } | {} + 'node-name': string; + 'uuid-interface': string; + } | {}; }; /** * Represents the component for Performance history application. */ -class PerformanceHistoryComponent extends React.Component{ +class PerformanceHistoryComponent extends React.Component { /** * Initialises this instance */ constructor(props: PerformanceHistoryComponentProps) { super(props); this.state = { - selectedNetworkElement: props.nodeId !== "" ? props.nodeId : "-1", - selectedTimePeriod: "15min", - selectedLtp: "-1", + selectedNetworkElement: props.nodeId !== '' ? props.nodeId : '-1', + selectedTimePeriod: '15min', + selectedLtp: '-1', showNetworkElementsTable: true, showLtps: false, showPanels: false, - preFilter: {} + preFilter: {}, }; } onChangeTabs = (event: React.SyntheticEvent, newValue: PanelId) => { const nextActivePanel = newValue; this.changeTabs(nextActivePanel); - } + }; changeTabs = (nextActivePanel: PanelId) => { this.props.setCurrentPanel(nextActivePanel); const preFilter = this.state.preFilter; switch (nextActivePanel) { - case "PerformanceData": + case 'PerformanceData': if (this.props.performanceData.preFilter !== {} && this.props.performanceData.preFilter === preFilter) { this.props.reloadPerformanceData(); } else { this.props.performanceDataPreActions.onPreFilterChanged(preFilter); } break; - case "ReceiveLevel": + case 'ReceiveLevel': if (this.props.receiveLevel.preFilter !== {} && this.props.receiveLevel.preFilter === preFilter) { this.props.reloadReceiveLevel(); - } - else { + } else { this.props.receiveLevelPreActions.onPreFilterChanged(preFilter); } break; - case "TransmissionPower": + case 'TransmissionPower': if (this.props.transmissionPower.preFilter !== {} && this.props.transmissionPower.preFilter === preFilter) { this.props.reloadTransmissionPower(); - } - else { + } else { this.props.transmissionPowerPreActions.onPreFilterChanged(preFilter); } break; - case "AdaptiveModulation": + case 'AdaptiveModulation': if (this.props.adaptiveModulation.preFilter !== {} && this.props.adaptiveModulation.preFilter === preFilter) { this.props.reloadAdaptiveModulation(); } else { this.props.adaptiveModulationPreActions.onPreFilterChanged(preFilter); } break; - case "Temperature": + case 'Temperature': if (this.props.temperature.preFilter !== {} && this.props.temperature.preFilter === preFilter) { this.props.reloadTemperature(); } else { this.props.temperaturePreActions.onPreFilterChanged(preFilter); } break; - case "SINR": + case 'SINR': if (this.props.signalToInterference.preFilter !== {} && this.props.signalToInterference.preFilter === preFilter) { this.props.reloadSignalToInterference(); } else { this.props.signalToInterferencePreActions.onPreFilterChanged(preFilter); } break; - case "CPD": + case 'CPD': if (this.props.crossPolarDiscrimination.preFilter !== {} && this.props.crossPolarDiscrimination.preFilter === preFilter) { this.props.reloadCrossPolarDiscrimination(); } else { @@ -219,21 +221,20 @@ class PerformanceHistoryComponent extends React.Component - { this.handleNetworkElementSelect(rowData.nodeId) }} columns={ - [{ property: "nodeId", title: "Node Name" }] + { this.handleNetworkElementSelect(rowData.nodeId); }} columns={ + [{ property: 'nodeId', title: 'Node Name' }] } /> - ) - } - else { + ); + } else { this.handleURLChange(nodeId); return ( <> @@ -259,42 +260,42 @@ class PerformanceHistoryComponent extends React.Component { - activePanel === "PerformanceData" && + activePanel === 'PerformanceData' && } { - activePanel === "ReceiveLevel" && + activePanel === 'ReceiveLevel' && } { - activePanel === "TransmissionPower" && + activePanel === 'TransmissionPower' && } { - activePanel === "AdaptiveModulation" && + activePanel === 'AdaptiveModulation' && } { - activePanel === "Temperature" && + activePanel === 'Temperature' && } { - activePanel === "SINR" && + activePanel === 'SINR' && } { - activePanel === "CPD" && + activePanel === 'CPD' && } } - ) + ); } } @@ -310,19 +311,19 @@ class PerformanceHistoryComponent extends React.Component { this.setState({ showPanels: true, - selectedLtp: firstLtp + selectedLtp: firstLtp, }); this.preFilterChangeAndReload(this.state.selectedNetworkElement, this.state.selectedTimePeriod, firstLtp); - this.changeTabs("PerformanceData"); - } + this.changeTabs('PerformanceData'); + }; /** * A function which reloads the visible table, if available, based on prefilters defined by network element and ltp on selected time period. */ private preFilterChangeAndReload = (networkElement: string, timePeriod: string, ltp: string) => { const newPreFilter = { - "node-name": networkElement, - "uuid-interface": ltp + 'node-name': networkElement, + 'uuid-interface': ltp, }; const activePanel = this.props.activePanel; @@ -331,25 +332,25 @@ class PerformanceHistoryComponent extends React.Component { @@ -391,12 +392,12 @@ class PerformanceHistoryComponent extends React.Component { if (ltpNotSelected) { this.setState({ - selectedLtp: "-1", - showPanels: false + selectedLtp: '-1', + showPanels: false, }); } - } + }; /** * Function which handles the time period changes. */ private handleTimePeriodChange = (event: SelectChangeEvent) => { - const selectedTimeInterval = event.target.value === "15min" + const selectedTimeInterval = event.target.value === '15min' ? PmDataInterval.pmInterval15Min : PmDataInterval.pmInterval24Hours; @@ -427,28 +428,28 @@ class PerformanceHistoryComponent extends React.Component ) => { - if (event.target.value === "-1") { + if (event.target.value === '-1') { this.setState({ showPanels: false, - selectedLtp: event.target.value + selectedLtp: event.target.value, }); } else if (event.target.value !== this.state.selectedLtp) { this.setState({ showPanels: true, - selectedLtp: event.target.value as string + selectedLtp: event.target.value as string, }); this.preFilterChangeAndReload(this.state.selectedNetworkElement, this.state.selectedTimePeriod, event.target.value as string); } - } + }; } const PerformanceHistoryApplication = withStyles(PerformanceHistoryComponentStyles)(connect(mapProps, mapDispatcher)(PerformanceHistoryComponent)); diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/tsconfig.json b/sdnr/wt/odlux/apps/performanceHistoryApp/tsconfig.json index a66b5d828..ca65092e0 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/tsconfig.json +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/tsconfig.json @@ -4,7 +4,7 @@ "outDir": "./dist", "sourceMap": true, "forceConsistentCasingInFileNames": true, - "allowSyntheticDefaultImports": false, + "allowSyntheticDefaultImports": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "noFallthroughCasesInSwitch": true, diff --git a/sdnr/wt/odlux/apps/performanceHistoryApp/webpack.config.js b/sdnr/wt/odlux/apps/performanceHistoryApp/webpack.config.js index 8f2192f12..2f25d0df1 100644 --- a/sdnr/wt/odlux/apps/performanceHistoryApp/webpack.config.js +++ b/sdnr/wt/odlux/apps/performanceHistoryApp/webpack.config.js @@ -57,6 +57,16 @@ module.exports = (env) => { use: [{ loader: "babel-loader" }] + },{ + //don't minify images + test: /\.(png|gif|jpg|svg)$/, + use: [{ + loader: 'url-loader', + options: { + limit: 10, + name: './images/[name].[ext]' + } + }] }] }, diff --git a/sdnr/wt/odlux/eslintrc.json b/sdnr/wt/odlux/eslintrc.json new file mode 100644 index 000000000..f98e65bcd --- /dev/null +++ b/sdnr/wt/odlux/eslintrc.json @@ -0,0 +1,54 @@ +{ + "root": true, + "parser": "@typescript-eslint/parser", + "plugins": [ + "import", + "@typescript-eslint", + "react", + "react-hooks" + ], + "extends": [ + "airbnb-typescript" + ], + "parserOptions": { + "project": [ + "./tsconfig.json" + ], + "sourceType": "module" + }, + "settings": { + "react": { + "version": "detect" + } + }, + "rules": { + "no-console": "off", + "no-debugger": "off", + "import/no-cycle": "off", + "quotes": [ "error", "single" ], + "import/prefer-default-export": "off", + "lines-between-class-members": "off", + "no-nested-ternary": "off", + "no-unused-vars": "off", + "object-curly-newline": "off", //["error", { "multiline": true, "minProperties": 8, "consistent": true }], + "max-len": [ 2, 280, 2, { "ignoreUrls": true } ], + "@typescript-eslint/lines-between-class-members": [ "error", "always", { "exceptAfterOverload": true } ], + "@typescript-eslint/quotes": [ "error", "single" , { "avoidEscape": true } ], + "@typescript-eslint/no-unused-vars": [ "error", { "argsIgnorePattern": "^_", "varsIgnorePattern": "^_", "caughtErrorsIgnorePattern": "^_" } ], + "@typescript-eslint/naming-convention": [ "error", + { "format": [ "camelCase", "PascalCase", "UPPER_CASE", "snake_case" ], "leadingUnderscore": "allow", "selector": "default", "filter": { "regex": "(^&)|(^\\w+(-\\w+)+)", "match": false } } ], + "no-underscore-dangle": [ "error", { "allowAfterThis": true } ], + "no-param-reassign": [ "error", { "props": false } ], + "react/prop-types": [ "error", { "skipUndeclared": true } ], + "@typescript-eslint/member-delimiter-style": ["error"] + }, + "overrides": [ + { + "files": "**/handlers/*Handler.ts", + "rules": { + "no-param-reassign": "off", + "@typescript-eslint/default-param-last": "off" + } + } + ] +} \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/pom.xml b/sdnr/wt/odlux/framework/pom.xml index 414f49827..3dd0f1a9d 100644 --- a/sdnr/wt/odlux/framework/pom.xml +++ b/sdnr/wt/odlux/framework/pom.xml @@ -19,6 +19,7 @@ ~ ============LICENSE_END======================================================= ~ --> + 4.0.0 @@ -45,7 +46,7 @@ ${maven.build.timestamp} ONAP Frankfurt (Neon, mdsal ${odl.mdsal.version}) - 142.63ceae1(22/01/31) + 178.3bd8a2a9(23/03/16) ONAP SDN-R | ONF Wireless for ${distversion} - Build: ${buildtime} ${buildno} ${project.version} @@ -102,7 +103,7 @@ initialize - v12.13.0 + v12.22.0 v1.22.10 diff --git a/sdnr/wt/odlux/framework/src/actions/settingsAction.ts b/sdnr/wt/odlux/framework/src/actions/settingsAction.ts index 1c18ddc8e..092b31814 100644 --- a/sdnr/wt/odlux/framework/src/actions/settingsAction.ts +++ b/sdnr/wt/odlux/framework/src/actions/settingsAction.ts @@ -19,7 +19,7 @@ import { Dispatch } from "../flux/store"; import { Action } from "../flux/action"; import { GeneralSettings, Settings, TableSettings, TableSettingsColumn } from "../models/settings"; -import { getSettings, putSettings } from "../services/settingsService"; +import { getUserdata, saveUserdata } from "../services/userdataService"; import { startWebsocketSession, suspendWebsocketSession } from "../services/notificationService"; import { IApplicationStoreState } from "../store/applicationStore"; @@ -67,7 +67,7 @@ export const setGeneralSettingsAction = (value: boolean) => (dispatcher: Dispatc export const updateGeneralSettingsAction = (activateNotifications: boolean) => async (dispatcher: Dispatch) => { const value: GeneralSettings = { general: { areNotificationsEnabled: activateNotifications } }; - const result = await putSettings("/general", JSON.stringify(value.general)); + const result = await saveUserdata("/general", JSON.stringify(value.general)); dispatcher(setGeneralSettingsAction(activateNotifications)); } @@ -86,14 +86,14 @@ export const updateTableSettings = (tableName: string, columns: TableSettingsCol // would only save latest entry //const json = JSON.stringify({ [tableName]: { columns: columns } }); - const result = await putSettings("/tables", json); + const result = await saveUserdata("/tables", json); dispatcher(new SetTableSettings(tableName, columns)); } export const getGeneralSettingsAction = () => async (dispatcher: Dispatch) => { - const result = await getSettings(); + const result = await getUserdata(); if (result && result.general) { dispatcher(new SetGeneralSettingsAction(result.general.areNotificationsEnabled!)) diff --git a/sdnr/wt/odlux/framework/src/app.tsx b/sdnr/wt/odlux/framework/src/app.tsx index 9b03a216d..bbe1f9ec8 100644 --- a/sdnr/wt/odlux/framework/src/app.tsx +++ b/sdnr/wt/odlux/framework/src/app.tsx @@ -32,14 +32,15 @@ import { applicationStoreCreator } from './store/applicationStore'; import { ApplicationStoreProvider } from './flux/connect'; import { startHistoryListener } from './middleware/navigation'; -import { startRestService } from './services/restService'; +import { startSoreService } from './services/storeService'; import { startUserSessionService } from './services/userSessionService'; import { startNotificationService } from './services/notificationService'; +import { startBroadcastChannel } from './services/broadcastService'; + import theme from './design/default'; import '!style-loader!css-loader!./app.css'; -import { startBroadcastChannel } from './services/broadcastService'; declare module '@mui/material/styles' { @@ -98,7 +99,7 @@ export const runApplication = () => { }; - startRestService(applicationStore); + startSoreService(applicationStore); startHistoryListener(applicationStore); startNotificationService(applicationStore); diff --git a/sdnr/wt/odlux/framework/src/assets/icons/About.svg b/sdnr/wt/odlux/framework/src/assets/icons/About.svg new file mode 100644 index 000000000..156e36efe --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/About.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/assets/icons/Home.svg b/sdnr/wt/odlux/framework/src/assets/icons/Home.svg new file mode 100644 index 000000000..0836714b4 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/Home.svg @@ -0,0 +1,28 @@ + + + + + + + + + + + + diff --git a/sdnr/wt/odlux/framework/src/assets/icons/Menu.svg b/sdnr/wt/odlux/framework/src/assets/icons/Menu.svg new file mode 100644 index 000000000..ea0312802 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/Menu.svg @@ -0,0 +1,18 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/assets/icons/Tools.svg b/sdnr/wt/odlux/framework/src/assets/icons/Tools.svg new file mode 100644 index 000000000..1595cdc1c --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/Tools.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + diff --git a/sdnr/wt/odlux/framework/src/assets/icons/User.svg b/sdnr/wt/odlux/framework/src/assets/icons/User.svg new file mode 100644 index 000000000..99618cf57 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/User.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png new file mode 100644 index 000000000..412390c79 Binary files /dev/null and b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.png differ diff --git a/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg new file mode 100644 index 000000000..c1d74bc96 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/assets/icons/ht.Connect.svg @@ -0,0 +1,81 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx b/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx index a04ab16af..d41d82687 100644 --- a/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx +++ b/sdnr/wt/odlux/framework/src/components/errorDisplay.tsx @@ -15,7 +15,7 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; import { Theme } from '@mui/material/styles'; import { WithStyles } from '@mui/styles'; @@ -31,7 +31,7 @@ import Typography from '@mui/material/Typography'; import { ClearErrorInfoAction, RemoveErrorInfoAction } from '../actions/errorActions'; -import connect, { Connect } from '../flux/connect'; +import { connect, Connect } from '../flux/connect'; const styles = (theme: Theme) => createStyles({ modal: { diff --git a/sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx b/sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx new file mode 100644 index 000000000..0d7d734c9 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/components/icons/menuIcon.tsx @@ -0,0 +1,29 @@ +import React from 'react'; + +type MenuIconPropsBase = { + className?: string; + size?: number | string; +}; + +type MenuIconPropsWithColor = MenuIconPropsBase & { + color: string; +}; + +type MenuIconProps = MenuIconPropsBase | MenuIconPropsWithColor; + +const MenuIcon = (props: MenuIconProps) => { + const { className, size = '30px' } = props; + const color = 'color' in props ? props.color : '#36A9E1'; + + return ( + + + + + + ); +}; + +MenuIcon.defaultName = 'MenuIcon'; + +export default MenuIcon; \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/components/logo.tsx b/sdnr/wt/odlux/framework/src/components/logo.tsx index b10cc8ce1..f2bb1f575 100644 --- a/sdnr/wt/odlux/framework/src/components/logo.tsx +++ b/sdnr/wt/odlux/framework/src/components/logo.tsx @@ -42,7 +42,7 @@ import withStyles from '@mui/styles/withStyles'; import createStyles from '@mui/styles/createStyles'; -import defaultLogo from '../assets/images/defaultLogo.svg'; +const defaultLogo = require('../assets/icons/ht.Connect.svg'); const styles = (theme: Theme) => createStyles({ headerLogo: { @@ -91,7 +91,7 @@ class LogoComponent extends React.Component { console.info([ "Logo hidden, because browser window width (", this.state.windowWidth, - "px) is lower thershold (", + "px) is lower threshold (", this.hideLogoWhenWindowWidthIsLower, "px)."].join('')); } diff --git a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx index 8541cfe56..c1a5005d4 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/index.tsx +++ b/sdnr/wt/odlux/framework/src/components/material-table/index.tsx @@ -450,13 +450,13 @@ class MaterialTableComponent extends React.Component=')) { - return valueAsNumber >= Number(filterExpressionAsString.substr(2).trim()); + return valueAsNumber >= Number(filterExpressionAsString.substring(2).trim()); } else if (filterExpressionAsString.startsWith('<=')) { - return valueAsNumber <= Number(filterExpressionAsString.substr(2).trim()); + return valueAsNumber <= Number(filterExpressionAsString.substring(2).trim()); } else if (filterExpressionAsString.startsWith('>')) { - return valueAsNumber > Number(filterExpressionAsString.substr(1).trim()); + return valueAsNumber > Number(filterExpressionAsString.substring(1).trim()); } else if (filterExpressionAsString.startsWith('<')) { - return valueAsNumber < Number(filterExpressionAsString.substr(1).trim()); + return valueAsNumber < Number(filterExpressionAsString.substring(1).trim()); } } else if (column.type === ColumnType.date){ const valueAsString = String(dataValue); @@ -480,13 +480,13 @@ class MaterialTableComponent extends React.Component=')) { - return valueAsDate >= convertToDate(filterExpressionAsString.substr(2).trim()); + return valueAsDate >= convertToDate(filterExpressionAsString.substring(2).trim()); } else if (filterExpressionAsString.startsWith('<=')) { - return valueAsDate <= convertToDate(filterExpressionAsString.substr(2).trim()); + return valueAsDate <= convertToDate(filterExpressionAsString.substring(2).trim()); } else if (filterExpressionAsString.startsWith('>')) { - return valueAsDate > convertToDate(filterExpressionAsString.substr(1).trim()); + return valueAsDate > convertToDate(filterExpressionAsString.substring(1).trim()); } else if (filterExpressionAsString.startsWith('<')) { - return valueAsDate < convertToDate(filterExpressionAsString.substr(1).trim()); + return valueAsDate < convertToDate(filterExpressionAsString.substring(1).trim()); } diff --git a/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx b/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx index f8ae6ea97..ab0d465e7 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx +++ b/sdnr/wt/odlux/framework/src/components/material-table/showColumnDialog.tsx @@ -16,9 +16,9 @@ * ============LICENSE_END========================================================================== */ -import { Button, Checkbox, FormControlLabel, MenuItem, Popover, Switch, Typography } from '@mui/material'; -import connect, { Connect, IDispatcher } from '../../flux/connect'; -import * as React from 'react'; +import React from 'react'; +import { Button, FormControlLabel, Popover, Switch, Typography } from '@mui/material'; +import { connect, Connect, IDispatcher } from '../../flux/connect'; import { ColumnModel } from './columnModel'; import { IApplicationStoreState } from '../../store/applicationStore'; diff --git a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts index f9015493f..e2fda7647 100644 --- a/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts +++ b/sdnr/wt/odlux/framework/src/components/material-table/utilities.ts @@ -51,6 +51,7 @@ export type ExternalMethodes = { onHandleChangeRowsPerPage: (rowsPerPage: number | null) => void; onHideColumns: (columnName: string[]) => void; onShowColumns: (columnName: string[]) => void; + onClearFilters: () => void; }, createPreActions: (dispatch: Dispatch, skipRefresh?: boolean) => { onPreFilterChanged: (preFilter: { @@ -328,7 +329,13 @@ export function createExternal(callback: DataCallback, selectState dispatch((dispatch: Dispatch) => { dispatch(new ShowColumnsAction(columnName)); }) - } + }, + onClearFilters: () => { + dispatch((dispatch: Dispatch) => { + let filter = { }; + dispatch(new SetFilterChangedAction(filter)); + }); + }, // selected: }; }; diff --git a/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx b/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx index 744cb0d24..626cb8978 100644 --- a/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx +++ b/sdnr/wt/odlux/framework/src/components/material-ui/listItemLink.tsx @@ -27,6 +27,8 @@ import { WithStyles } from '@mui/styles'; import withStyles from '@mui/styles/withStyles'; import createStyles from '@mui/styles/createStyles'; import { toAriaLabel } from '../../utilities/yangHelper'; +import { IconType } from '../../models/iconDefinition'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; const styles = (theme: Theme) => createStyles({ active: { @@ -35,7 +37,7 @@ const styles = (theme: Theme) => createStyles({ }); export interface IListItemLinkProps extends WithStyles { - icon: JSX.Element | null; + icon: IconType | null; primary: string | React.ComponentType; secondary?: React.ComponentType; to: string; @@ -48,13 +50,19 @@ export const ListItemLink = withStyles(styles)((props: IListItemLinkProps) => { const renderLink = (itemProps: any): JSX.Element => ( props.external ? : ); - + + const customIconHeight = 22; const ariaLabel = typeof Primary === 'string' ? toAriaLabel("link-to-"+Primary) : toAriaLabel("link-to-"+Primary.displayName); + + //create menu icon, either using an faIcon or a link to a custom svg icon + //moved to one place for easier usage + const listItemIcon = icon && ( typeof icon === 'string' ? : ); + return ( <> { icon - ? { icon } + ? { listItemIcon } : null } { typeof Primary === 'string' diff --git a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx index 195706d28..d969286b7 100644 --- a/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx +++ b/sdnr/wt/odlux/framework/src/components/navigationMenu.tsx @@ -15,15 +15,14 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; import { Theme } from '@mui/material/styles'; +import classNames from 'classnames'; import { WithStyles } from '@mui/styles'; import withStyles from '@mui/styles/withStyles'; import createStyles from '@mui/styles/createStyles'; -import { faHome, faAddressBook } from '@fortawesome/free-solid-svg-icons'; - import Drawer from '@mui/material/Drawer'; import List from '@mui/material/List'; @@ -34,11 +33,14 @@ import { faProjectDiagram } from '@fortawesome/free-solid-svg-icons'; import ListItemLink from '../components/material-ui/listItemLink'; -import connect, { Connect } from '../flux/connect'; +import { connect, Connect } from '../flux/connect'; import { MenuAction } from '../actions/menuAction'; -import * as classNames from 'classnames'; import { transportPCEUrl } from '../app'; +const aboutIcon = require('../assets/icons/About.svg'); +const homeIcon = require('../assets/icons/Home.svg'); +const loginIcon = require('../assets/icons/User.svg'); +const settingsIcon = require('../assets/icons/Tools.svg'); const drawerWidth = 240; @@ -141,16 +143,15 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di window.dispatchEvent(new Event('menu-resized')); }, [isOpen]) - let menuItems = state.framework.applicationRegistraion && Object.keys(state.framework.applicationRegistraion).map(key => { - const reg = state.framework.applicationRegistraion[key]; - const icon = !reg.icon ? null :( typeof reg.icon === 'string' ? : ) + let menuItems = state.framework.applicationRegistration && Object.keys(state.framework.applicationRegistration).map(key => { + const reg = state.framework.applicationRegistration[key]; return reg && ( + icon={reg.icon || null} /> ) || null; }) || null; @@ -160,7 +161,7 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di key={"transportPCE"} to={transportUrl} primary={"TransportPCE"} - icon={} + icon={faProjectDiagram} external />; const linkFound = menuItems.find(obj => obj.key === "linkCalculation"); @@ -191,17 +192,17 @@ export const NavigationMenu = withStyles(styles)(connect()(({ classes, state, di
    { /* https://fiffty.github.io/react-treeview-mui/ */} - } /> + { menuItems } - } /> + {(false && process.env.NODE_ENV === "development") ? <> - } /> + : null } diff --git a/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx b/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx index d055b8a87..aa22f17f4 100644 --- a/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx +++ b/sdnr/wt/odlux/framework/src/components/routing/appFrame.tsx @@ -15,9 +15,9 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; -import connect, { Connect } from '../../flux/connect'; +import { connect, Connect } from '../../flux/connect'; import { SetTitleAction } from '../../actions/titleActions'; import { AddErrorInfoAction } from '../../actions/errorActions'; diff --git a/sdnr/wt/odlux/framework/src/components/settings/general.tsx b/sdnr/wt/odlux/framework/src/components/settings/general.tsx index 90f15c1d2..ffd516ba1 100644 --- a/sdnr/wt/odlux/framework/src/components/settings/general.tsx +++ b/sdnr/wt/odlux/framework/src/components/settings/general.tsx @@ -16,11 +16,11 @@ * ============LICENSE_END========================================================================== */ +import React from 'react'; import { Button, FormControlLabel, Switch, Typography } from '@mui/material'; import makeStyles from '@mui/styles/makeStyles'; import { SettingsComponentProps } from '../../models/settings'; -import * as React from 'react'; -import connect, { Connect, IDispatcher } from '../../flux/connect'; +import { connect, Connect, IDispatcher } from '../../flux/connect'; import { IApplicationStoreState } from '../../store/applicationStore'; import { getGeneralSettingsAction, SetGeneralSettingsAction, updateGeneralSettingsAction } from '../../actions/settingsAction'; import { sendMessage, SettingsMessage } from '../../services/broadcastService'; diff --git a/sdnr/wt/odlux/framework/src/components/titleBar.tsx b/sdnr/wt/odlux/framework/src/components/titleBar.tsx index 19d3bdf74..40c0fc736 100644 --- a/sdnr/wt/odlux/framework/src/components/titleBar.tsx +++ b/sdnr/wt/odlux/framework/src/components/titleBar.tsx @@ -15,7 +15,7 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React from 'react'; import { withRouter, RouteComponentProps } from 'react-router-dom'; import { Theme } from '@mui/material/styles'; @@ -27,9 +27,6 @@ import Toolbar from '@mui/material/Toolbar'; import Typography from '@mui/material/Typography'; import Button from '@mui/material/Button'; import IconButton from '@mui/material/IconButton'; -import Block from '@mui/icons-material/Block'; -import Adjust from '@mui/icons-material/Adjust'; -import MenuIcon from '@mui/icons-material/Menu'; import AccountCircle from '@mui/icons-material/AccountCircle'; import MenuItem from '@mui/material/MenuItem'; import Menu from '@mui/material/Menu'; @@ -41,10 +38,12 @@ import { faDotCircle } from '@fortawesome/free-solid-svg-icons'; import { logoutUser } from '../actions/authentication'; import { PushAction, ReplaceAction } from '../actions/navigationActions'; -import connect, { Connect, IDispatcher } from '../flux/connect'; -import Logo from './logo'; +import { connect, Connect, IDispatcher } from '../flux/connect'; import { MenuAction, MenuClosedByUser } from '../actions/menuAction'; +import MenuIcon from './icons/menuIcon'; +import Logo from './logo'; + const styles = (theme: Theme) => createStyles({ appBar: { zIndex: theme.zIndex.drawer + 1, @@ -58,7 +57,8 @@ const styles = (theme: Theme) => createStyles({ }, icon: { marginLeft: 16, - marginRight: 8 + marginRight: 8, + marginBottom: -2, }, connected: { color: "green" @@ -112,10 +112,10 @@ class TitleBarComponent extends React.Component { - const reg = state.framework.applicationRegistraion[key]; + Object.keys(state.framework.applicationRegistration).map(key => { + const reg = state.framework.applicationRegistration[key]; if (reg && reg.statusBarElement) { if (key === "help") { isNotificationInfoAdded = true; @@ -132,7 +132,12 @@ class TitleBarComponent extends React.Component : ) + const customIconHeight = 22; + const icon = !stateIcon + ? null + : (typeof stateIcon === 'string' + ? + : ) return ( diff --git a/sdnr/wt/odlux/framework/src/flux/connect.ts b/sdnr/wt/odlux/framework/src/flux/connect.ts deleted file mode 100644 index f54e4e0f0..000000000 --- a/sdnr/wt/odlux/framework/src/flux/connect.ts +++ /dev/null @@ -1,161 +0,0 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 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 PropTypes from 'prop-types'; - -import { Dispatch } from '../flux/store'; - -import { ApplicationStore, IApplicationStoreState } from '../store/applicationStore'; - -interface IApplicationStoreContext { - applicationStore: ApplicationStore; -} - -export interface IDispatcher { - dispatch: Dispatch; -} - -interface IApplicationStoreProps { - state: IApplicationStoreState; -} - -interface IDispatchProps { - dispatch: Dispatch; -} - -type Omit = Pick> - -type ComponentDecoratorInfer = { - (wrappedComponent: React.ComponentType): React.ComponentClass>; -}; - -export type Connect any) | undefined = undefined, TMapDispatch extends ((...args: any) => any) | undefined = undefined> = - (TMapProps extends ((...args: any) => any) ? ReturnType : IApplicationStoreProps) & - (TMapDispatch extends ((...args: any) => any) ? ReturnType : IDispatchProps); - -export function connect(): ComponentDecoratorInfer; - -export function connect( - mapStateToProps: (state: IApplicationStoreState) => TStateProps -): ComponentDecoratorInfer; - -export function connect( - mapStateToProps: (state: IApplicationStoreState) => TStateProps, - mapDispatchToProps: (dispatcher: IDispatcher) => TDispatchProps -): ComponentDecoratorInfer; - - -export function connect( - mapStateToProps: undefined, - mapDispatchToProps: (dispatcher: IDispatcher) => TDispatchProps -): ComponentDecoratorInfer; - - -export function connect( - mapStateToProps?: ((state: IApplicationStoreState) => TStateProps), - mapDispatchToProps?: ((dispatcher: IDispatcher) => TDispatchProps) -): - ((WrappedComponent: React.ComponentType) => React.ComponentType) { - - const injectApplicationStore = (WrappedComponent: React.ComponentType): React.ComponentType => { - - class StoreAdapter extends React.Component { - public static contextTypes = { ...WrappedComponent.contextTypes, applicationStore: PropTypes.object.isRequired }; - context: IApplicationStoreContext; - - render(): JSX.Element { - - if (isWrappedComponentIsVersion1(WrappedComponent)) { - const element = React.createElement(WrappedComponent, { ...(this.props as any), state: this.store.state, dispatch: this.store.dispatch.bind(this.store) }); - return element; - } else if (mapStateToProps && isWrappedComponentIsVersion2(WrappedComponent)) { - const element = React.createElement(WrappedComponent, { ...(this.props as any), ...(mapStateToProps(this.store.state) as any), dispatch: this.store.dispatch.bind(this.store) }); - return element; - } else if (mapStateToProps && mapDispatchToProps && isWrappedComponentIsVersion3(WrappedComponent)) { - const element = React.createElement(WrappedComponent, { ...(this.props as any), ...(mapStateToProps(this.store.state) as any), ...(mapDispatchToProps({ dispatch: this.store.dispatch.bind(this.store) }) as any) }); - return element; - } else if (!mapStateToProps && mapDispatchToProps && isWrappedComponentIsVersion4(WrappedComponent)) { - const element = React.createElement(WrappedComponent, { ...(this.props as any), state: this.store.state, ...(mapDispatchToProps({ dispatch: this.store.dispatch.bind(this.store) }) as any) }); - return element; - } - throw new Error("Invalid arguments in connect."); - } - - componentDidMount(): void { - this.store && this.store.changed.addHandler(this.handleStoreChanged); - } - - componentWillUnmount(): void { - this.store && this.store.changed.removeHandler(this.handleStoreChanged); - } - - private get store(): ApplicationStore { - return this.context.applicationStore; - } - - private handleStoreChanged = () => { - this.forceUpdate(); - } - } - - return StoreAdapter; - } - - - return injectApplicationStore; - - /* inline methods */ - - function isWrappedComponentIsVersion1(wrappedComponent: any): wrappedComponent is React.ComponentType { - return !mapStateToProps && !mapDispatchToProps; - } - - function isWrappedComponentIsVersion2(wrappedComponent: any): wrappedComponent is React.ComponentType { - return !!mapStateToProps && !mapDispatchToProps; - } - - function isWrappedComponentIsVersion3(wrappedComponent: any): wrappedComponent is React.ComponentType { - return !!mapStateToProps && !!mapDispatchToProps; - } - - function isWrappedComponentIsVersion4(wrappedComponent: any): wrappedComponent is React.ComponentType { - return !mapStateToProps && !!mapDispatchToProps; - } -} - -interface ApplicationStoreProviderProps extends React.Props { - applicationStore: ApplicationStore; -} - -export class ApplicationStoreProvider extends React.Component - implements /* React.ComponentLifecycle, */ React.ChildContextProvider { - - public static childContextTypes = { applicationStore: PropTypes.object.isRequired }; - - getChildContext(): IApplicationStoreContext { - return { - applicationStore: this.props.applicationStore - }; - } - - render(): JSX.Element { - return React.Children.only(this.props.children) as any; //type error, fix when possible - } -} - -export default connect; \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/flux/connect.tsx b/sdnr/wt/odlux/framework/src/flux/connect.tsx new file mode 100644 index 000000000..09d30dae7 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/flux/connect.tsx @@ -0,0 +1,213 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2019 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + * ============LICENSE_END========================================================================== + */ +import React, { FC, useContext, createContext, useState, useEffect, useRef } from 'react'; + +import { Dispatch } from './store'; + +import { ApplicationStore, IApplicationStoreState } from '../store/applicationStore'; + +const LogLevel = +(localStorage.getItem('log.odlux.framework.flux.connect') || 0); + +interface IApplicationStoreContext { + applicationStore: ApplicationStore; +} + +export interface IDispatcher { + dispatch: Dispatch; +} + +interface IApplicationStoreProps { + state: IApplicationStoreState; +} + +interface IDispatchProps { + dispatch: Dispatch; +} + +type ComponentDecoratorInfer = { + (wrappedComponent: React.ComponentType): React.ComponentClass>; +}; + +const ApplicationStoreContext = createContext(undefined); + +export type Connect any) | undefined = undefined, TMapDispatch extends ((...args: any) => any) | undefined = undefined> = + (TMapProps extends ((...args: any) => any) ? ReturnType : IApplicationStoreProps) & + (TMapDispatch extends ((...args: any) => any) ? ReturnType : IDispatchProps); + +export function connect(): ComponentDecoratorInfer; + +export function connect( + mapStateToProps: (state: IApplicationStoreState) => TStateProps +): ComponentDecoratorInfer; + +export function connect( + mapStateToProps: (state: IApplicationStoreState) => TStateProps, + mapDispatchToProps: (dispatcher: IDispatcher) => TDispatchProps +): ComponentDecoratorInfer; + + +export function connect( + mapStateToProps: undefined, + mapDispatchToProps: (dispatcher: IDispatcher) => TDispatchProps +): ComponentDecoratorInfer; + + +export function connect( + mapStateToProps?: ((state: IApplicationStoreState) => TStateProps), + mapDispatchToProps?: ((dispatcher: IDispatcher) => TDispatchProps) +): + ((WrappedComponent: React.ComponentType) => React.ComponentType) { + + const injectApplicationStore = (WrappedComponent: React.ComponentType): React.ComponentType => { + + class StoreAdapter extends React.Component { + + render(): JSX.Element { + + if (isWrappedComponentIsVersion1(WrappedComponent)) { + const element = React.createElement(WrappedComponent, { ...(this.props as any), state: this.store.state, dispatch: this.store.dispatch.bind(this.store) }); + return element; + } else if (mapStateToProps && isWrappedComponentIsVersion2(WrappedComponent)) { + const element = React.createElement(WrappedComponent, { ...(this.props as any), ...(mapStateToProps(this.store.state) as any), dispatch: this.store.dispatch.bind(this.store) }); + return element; + } else if (mapStateToProps && mapDispatchToProps && isWrappedComponentIsVersion3(WrappedComponent)) { + const element = React.createElement(WrappedComponent, { ...(this.props as any), ...(mapStateToProps(this.store.state) as any), ...(mapDispatchToProps({ dispatch: this.store.dispatch.bind(this.store) }) as any) }); + return element; + } else if (!mapStateToProps && mapDispatchToProps && isWrappedComponentIsVersion4(WrappedComponent)) { + const element = React.createElement(WrappedComponent, { ...(this.props as any), state: this.store.state, ...(mapDispatchToProps({ dispatch: this.store.dispatch.bind(this.store) }) as any) }); + return element; + } + throw new Error("Invalid arguments in connect."); + } + + componentDidMount(): void { + this.store && this.store.changed.addHandler(this.handleStoreChanged); + } + + componentWillUnmount(): void { + this.store && this.store.changed.removeHandler(this.handleStoreChanged); + } + + private get store(): ApplicationStore { + return this.context.applicationStore; + } + + private handleStoreChanged = () => { + this.forceUpdate(); + } + } + StoreAdapter.contextType = ApplicationStoreContext; + return StoreAdapter; + } + + + return injectApplicationStore; + + /* inline methods */ + + function isWrappedComponentIsVersion1(wrappedComponent: any): wrappedComponent is React.ComponentType { + return !mapStateToProps && !mapDispatchToProps; + } + + function isWrappedComponentIsVersion2(wrappedComponent: any): wrappedComponent is React.ComponentType { + return !!mapStateToProps && !mapDispatchToProps; + } + + function isWrappedComponentIsVersion3(wrappedComponent: any): wrappedComponent is React.ComponentType { + return !!mapStateToProps && !!mapDispatchToProps; + } + + function isWrappedComponentIsVersion4(wrappedComponent: any): wrappedComponent is React.ComponentType { + return !mapStateToProps && !!mapDispatchToProps; + } +} + +type ApplicationStoreProviderProps = { + applicationStore: ApplicationStore; +} + +export const ApplicationStoreProvider: FC = (props) => { + const { applicationStore, children } = props; + + return ( + + {children} + + ); +}; + +export const useApplicationStore = (): ApplicationStore => { + const context = useContext(ApplicationStoreContext); + if (context == null || context.applicationStore == null) { + throw new Error("Requires application store provider!") + } + return context.applicationStore +}; + +export const useSelectApplicationState = ( selector: (state: IApplicationStoreState) => TProp, eqFunc = (a: TProp, b: TProp) => a === b ): TProp => { + const context = useContext(ApplicationStoreContext); + if (context == null || context.applicationStore == null) { + throw new Error("Requires application store provider!") + } + + const [propState, setPropState] = useState(selector(context.applicationStore.state)); + + const selectorRef = useRef(selector); + selectorRef.current = selector; + + const propStateRef = useRef({propState}); + propStateRef.current.propState = propState; + + useEffect(() => { + if (context == null || context.applicationStore == null) { + throw new Error("Requires application store provider!") + } + + const changedHandler = () => { + const newState = selectorRef.current(context.applicationStore.state); + if (!eqFunc(newState, propStateRef.current.propState)) { + setPropState(newState); + } + }; + + if (LogLevel > 3) { + console.log("useSelectApplicationState: adding handler", changedHandler); + } + + context.applicationStore.changed.addHandler(changedHandler); + + return () => { + if (LogLevel > 3) { + console.log("useSelectApplicationState: removing handler", changedHandler); + } + + context.applicationStore.changed.removeHandler(changedHandler); + } + }, [context]); + + return propState; + +}; + +export const useApplicationDispatch = (): Dispatch => { + const context = useContext(ApplicationStoreContext); + if (context == null || context.applicationStore == null) { + throw new Error("Requires application store provider!") + } + return context.applicationStore.dispatch; +}; diff --git a/sdnr/wt/odlux/framework/src/flux/store.ts b/sdnr/wt/odlux/framework/src/flux/store.ts index dd80ce7ba..347d295e0 100644 --- a/sdnr/wt/odlux/framework/src/flux/store.ts +++ b/sdnr/wt/odlux/framework/src/flux/store.ts @@ -20,6 +20,8 @@ import { Event } from "../common/event" import { Action } from './action'; import { IActionHandler } from './action'; +const LogLevel = +(localStorage.getItem('log.odlux.framework.flux.store') || 0); + export interface Dispatch { (action: TAction): TAction; } @@ -28,8 +30,8 @@ export interface Enhancer { (store: Store): Dispatch; } -class InitialisationAction extends Action { }; -const initialisationAction = new InitialisationAction(); +class InitializationAction extends Action { }; +const initializationAction = new InitializationAction(); export class Store { @@ -43,19 +45,22 @@ export class Store { this._isDispatching = false; - this.changed = new Event(); // sollten wir hier eventuell sogar den state mit übergeben ? + this.changed = new Event(); this._actionHandler = actionHandler; this._state = initialState as TStoreState; if (enhancer) this._dispatch = enhancer(this); - this._dispatch(initialisationAction); + this._dispatch(initializationAction); } public changed: Event; private _dispatch: Dispatch = (payload: TAction): TAction => { + if (LogLevel > 2) { + console.log('Store::Dispatch - ', payload); + } if (payload == null || !(payload instanceof Action)) { throw new Error( 'Actions must inherit from type Action. ' + @@ -76,6 +81,9 @@ export class Store { } if (this._state !== oldState) { + if (LogLevel > 3) { + console.log('Store::Dispatch - state has changed', this._state); + } this.changed.invoke(); } diff --git a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts index f98d77487..71b9e33d1 100644 --- a/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts +++ b/sdnr/wt/odlux/framework/src/handlers/authenticationHandler.ts @@ -23,7 +23,7 @@ import { AuthPolicy, User } from '../models/authentication'; import { onLogin, onLogout } from '../services/applicationApi'; import { startWebsocketSession, endWebsocketSession } from '../services/notificationService'; import { startUserSession, endUserSession } from '../services/userSessionService'; -import { getSettings } from '../services/settingsService'; +import { getUserdata } from '../services/userdataService'; export interface IAuthenticationState { user?: User; diff --git a/sdnr/wt/odlux/framework/src/middleware/logger.ts b/sdnr/wt/odlux/framework/src/middleware/logger.ts index 47725e696..fb0874f3e 100644 --- a/sdnr/wt/odlux/framework/src/middleware/logger.ts +++ b/sdnr/wt/odlux/framework/src/middleware/logger.ts @@ -18,16 +18,17 @@ import { Dispatch } from '../flux/store'; import { MiddlewareApi } from '../store/applicationStore'; +const LogLevel = +(localStorage.getItem('log.odlux.framework.middleware.logger') || 0); function createLoggerMiddleware() { return function logger({ getState }: MiddlewareApi) { return (next: Dispatch): Dispatch => action => { - console.log('will dispatch', action); + if (LogLevel > 2) console.log('will dispatch', action); const returnValue = next(action); - console.log('state after dispatch', getState()); + if (LogLevel > 2) console.log('state after dispatch', getState()); return returnValue; }; - } + }; } export const logger = createLoggerMiddleware(); diff --git a/sdnr/wt/odlux/framework/src/middleware/navigation.ts b/sdnr/wt/odlux/framework/src/middleware/navigation.ts index 5f3eed55d..44035fec3 100644 --- a/sdnr/wt/odlux/framework/src/middleware/navigation.ts +++ b/sdnr/wt/odlux/framework/src/middleware/navigation.ts @@ -55,7 +55,7 @@ const routerMiddlewareCreator = (history: History) => () => (next: Dispatch): Di // ensure user is logged in and token is valid if (action.pathname.startsWith("/oauth") && (action.search.startsWith("?token="))){ const ind = action.search.lastIndexOf("token="); - const tokenStr = ind > -1 ? action.search.substr(ind+6) : null; + const tokenStr = ind > -1 ? action.search.substring(ind+6) : null; const token = tokenStr && jwt.decode(tokenStr); if (tokenStr && token) { // @ts-ignore diff --git a/sdnr/wt/odlux/framework/src/services/applicationApi.ts b/sdnr/wt/odlux/framework/src/services/applicationApi.ts index 36523f9eb..8246ee8fa 100644 --- a/sdnr/wt/odlux/framework/src/services/applicationApi.ts +++ b/sdnr/wt/odlux/framework/src/services/applicationApi.ts @@ -15,13 +15,10 @@ * the License. * ============LICENSE_END========================================================================== */ -import { GeneralSettings } from '../models/settings'; -import { setGeneralSettingsAction, SetGeneralSettingsAction } from '../actions/settingsAction'; + import { Event } from '../common/event'; import { ApplicationStore } from '../store/applicationStore'; import { AuthMessage, getBroadcastChannel, sendMessage } from './broadcastService'; -import { endWebsocketSession } from './notificationService'; -import { getSettings } from './settingsService'; let resolveApplicationStoreInitialized: (store: ApplicationStore) => void; let applicationStore: ApplicationStore | null = null; diff --git a/sdnr/wt/odlux/framework/src/services/applicationManager.ts b/sdnr/wt/odlux/framework/src/services/applicationManager.ts index 14e3ed0b2..bd620a020 100644 --- a/sdnr/wt/odlux/framework/src/services/applicationManager.ts +++ b/sdnr/wt/odlux/framework/src/services/applicationManager.ts @@ -23,7 +23,7 @@ import { applicationApi } from './applicationApi'; /** Represents registry to manage all applications. */ class ApplicationManager { - /** Stores all registerd applications. */ + /** Stores all registered applications. */ private _applications: { [key: string]: ApplicationInfo }; /** Initializes a new instance of this class. */ @@ -32,7 +32,7 @@ class ApplicationManager { this.changed = new Event(); } - /** The chaged event will fire if the registration has changed. */ + /** The changed event will fire if the registration has changed. */ public changed: Event; /** Registers a new application. */ diff --git a/sdnr/wt/odlux/framework/src/services/broadcastService.ts b/sdnr/wt/odlux/framework/src/services/broadcastService.ts index f2c3ebc57..40968e54f 100644 --- a/sdnr/wt/odlux/framework/src/services/broadcastService.ts +++ b/sdnr/wt/odlux/framework/src/services/broadcastService.ts @@ -22,89 +22,95 @@ import { ReplaceAction } from "../actions/navigationActions"; import { User } from "../models/authentication"; import { ApplicationStore } from "../store/applicationStore"; -type Broadcaster = {channel: BroadcastChannel, key: String}; +type Broadcaster = { + channel: BroadcastChannel; + key: String; +}; type AuthTypes = 'login' | 'logout'; -export type AuthMessage={key: AuthTypes, data: any}; +export type AuthMessage = { + key: AuthTypes; + data: any; +}; type SettingsType = 'general'; -export type SettingsMessage={key: SettingsType, enableNotifications: boolean, user: string}; +export type SettingsMessage = { + key: SettingsType; + enableNotifications: boolean; + user: string; +}; -let channels: Broadcaster[] = []; -let store : ApplicationStore | null = null; +const channels: Broadcaster[] = []; +let store: ApplicationStore | null = null; export const saveChannel = (channel: BroadcastChannel, channelName: string) => { - channels.push({channel: channel, key: channelName}); -} - -export const startBroadcastChannel = (applicationStore: ApplicationStore)=>{ - store=applicationStore; - - //might decide to use one general broadcast channel with more keys in the future - createAuthBroadcastChannel(); - createSettingsBroadcastChannel(); -} - -const createSettingsBroadcastChannel = () =>{ - - const name = "odlux_settings"; - const bc: BroadcastChannel = new BroadcastChannel(name); - channels.push({ channel: bc, key: name }); - - bc.onmessage = (eventMessage: MessageEvent) => { - console.log(eventMessage) - - if (eventMessage.data.key === 'general') { - - if (store?.state.framework.authenticationState.user) { - const data = eventMessage.data; - if(store.state.framework.authenticationState.user.user === data.user){ - store?.dispatch(setGeneralSettingsAction(data.enableNotifications)); - } - } - } - } + channels.push({ channel: channel, key: channelName }); +}; -} +export const startBroadcastChannel = (applicationStore: ApplicationStore) => { + store = applicationStore; -const createAuthBroadcastChannel = () => { - const name = "odlux_auth"; - const bc: BroadcastChannel = new BroadcastChannel(name); - channels.push({ channel: bc, key: name }); - - bc.onmessage = (eventMessage: MessageEvent) => { - console.log(eventMessage) - - if (eventMessage.data.key === 'login') { - if (!store?.state.framework.authenticationState.user) { - const initialToken = localStorage.getItem("userToken"); - if (initialToken) { - store?.dispatch(loginUserAction(User.fromString(initialToken))); - store?.dispatch(new ReplaceAction("/")); - } - } - } - else if (eventMessage.data.key === 'logout') { + //might decide to use one general broadcast channel with more keys in the future + createAuthBroadcastChannel(); + createSettingsBroadcastChannel(); +}; - if (store?.state.framework.authenticationState.user) { - store?.dispatch(logoutUser()); - store?.dispatch(new ReplaceAction("/login")); - } - } - } -} +const createSettingsBroadcastChannel = () => { -export const getBroadcastChannel = (channelName: string) =>{ - const foundChannel = channels.find(s =>s.key===channelName); - return foundChannel?.channel; -} + const name = "odlux_settings"; + const bc: BroadcastChannel = new BroadcastChannel(name); + channels.push({ channel: bc, key: name }); + bc.onmessage = (eventMessage: MessageEvent) => { + console.log(eventMessage); -export const sendMessage = (data: any, channel: string) =>{ + if (eventMessage.data.key === 'general') { - const foundChannel = channels.find(s =>s.key===channel); - if(foundChannel){ - foundChannel.channel.postMessage(data); - } + if (store?.state.framework.authenticationState.user) { + const data = eventMessage.data; + if (store.state.framework.authenticationState.user.user === data.user) { + store?.dispatch(setGeneralSettingsAction(data.enableNotifications)); + } + } + } + } +}; + +const createAuthBroadcastChannel = () => { + const name = "odlux_auth"; + const bc: BroadcastChannel = new BroadcastChannel(name); + channels.push({ channel: bc, key: name }); + + bc.onmessage = (eventMessage: MessageEvent) => { + console.log(eventMessage) + + if (eventMessage.data.key === 'login') { + if (!store?.state.framework.authenticationState.user) { + const initialToken = localStorage.getItem("userToken"); + if (initialToken) { + store?.dispatch(loginUserAction(User.fromString(initialToken))); + store?.dispatch(new ReplaceAction("/")); + } + } + } + else if (eventMessage.data.key === 'logout') { + if (store?.state.framework.authenticationState.user) { + store?.dispatch(logoutUser()); + store?.dispatch(new ReplaceAction("/login")); + } } + } +}; + +export const getBroadcastChannel = (channelName: string) => { + const foundChannel = channels.find(s => s.key === channelName); + return foundChannel?.channel; +}; + +export const sendMessage = (data: any, channel: string) => { + const foundChannel = channels.find(s => s.key === channel); + if (foundChannel) { + foundChannel.channel.postMessage(data); + } +}; diff --git a/sdnr/wt/odlux/framework/src/services/index.ts b/sdnr/wt/odlux/framework/src/services/index.ts index 19b451345..85f0708a6 100644 --- a/sdnr/wt/odlux/framework/src/services/index.ts +++ b/sdnr/wt/odlux/framework/src/services/index.ts @@ -18,5 +18,5 @@ export { applicationManager } from './applicationManager'; export { subscribe, unsubscribe } from './notificationService'; export { requestRest } from './restService'; -export { putSettings, getSettings} from './settingsService'; +export { saveUserdata, getUserdata } from './userdataService'; diff --git a/sdnr/wt/odlux/framework/src/services/restService.ts b/sdnr/wt/odlux/framework/src/services/restService.ts index d727e4c9e..a296c52a4 100644 --- a/sdnr/wt/odlux/framework/src/services/restService.ts +++ b/sdnr/wt/odlux/framework/src/services/restService.ts @@ -16,18 +16,13 @@ * ============LICENSE_END========================================================================== */ +import { ReplaceAction } from '../actions/navigationActions'; +import { AddErrorInfoAction } from '../actions/errorActions'; -import { ApplicationStore } from "../store/applicationStore"; -import { ReplaceAction } from "../actions/navigationActions"; -import { AddErrorInfoAction } from "../actions/errorActions"; +import { storeService } from './storeService'; const baseUri = `${ window.location.origin }`; const absUrlPattern = /^https?:\/\//; -let applicationStore: ApplicationStore | null = null; - -export const startRestService = (store: ApplicationStore) => { - applicationStore = store; -}; export const formEncode = (params: { [key: string]: string | number }) => Object.keys(params).map((key) => { return encodeURIComponent(key) + '=' + encodeURIComponent(params[key].toString()); @@ -46,9 +41,9 @@ export const getAccessPolicyByUrl = (url: string) => { DELETE: false, }; - if (!applicationStore) return result; + if (!storeService.applicationStore) return result; - const { state: { framework: { applicationState: { enablePolicy }, authenticationState: { policies }}} } = applicationStore!; + const { state: { framework: { applicationState: { enablePolicy }, authenticationState: { policies } } } } = storeService.applicationStore!; result.GET = true; result.POST = true; @@ -71,7 +66,7 @@ export const getAccessPolicyByUrl = (url: string) => { return result; -} +}; /** Sends a rest request to the given path. * @returns The data, or null it there was any error @@ -87,8 +82,8 @@ export async function requestRest(path: string = '', init: RequestInit = /** Sends a rest request to the given path and reports the server state. * @returns An object with the server state, a message and the data or undefined in case of a json parse error. */ -export async function requestRestExt(path: string = '', init: RequestInit = {}, authenticate: boolean = true, isResource: boolean = false): Promise<{ status: number, message?: string, data: TData | null | undefined }> { - const result: { status: number, message?: string, data: TData | null } = { +export async function requestRestExt(path: string = '', init: RequestInit = {}, authenticate: boolean = true, isResource: boolean = false): Promise<{ status: number; message?: string; data: TData | null | undefined }> { + const result: { status: number; message?: string; data: TData | null } = { status: -1, data: null, }; @@ -100,60 +95,59 @@ export async function requestRestExt(path: string = '', init: RequestInit headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', - ...init.headers - } + ...init.headers, + }, }; - if (!isAbsUrl && authenticate && applicationStore) { - const { state: { framework: { authenticationState: { user } } } } = applicationStore; + if (!isAbsUrl && authenticate && storeService.applicationStore) { + const { state: { framework: { authenticationState: { user } } } } = storeService.applicationStore; // do not request if the user is not valid if (!user || !user.isValid) { return { ...result, - message: "User is not valid or not logged in." + message: 'User is not valid or not logged in.', }; } (init.headers = { ...init.headers, - 'Authorization': `${user.tokenType} ${user.token}` + 'Authorization': `${user.tokenType} ${user.token}`, //'Authorization': 'Basic YWRtaW46YWRtaW4=' }); } const fetchResult = await fetch(uri, init); - if(fetchResult.status === 403){ - applicationStore && applicationStore.dispatch(new AddErrorInfoAction({title: "Forbidden", message:"Status: [403], access denied."})); + if (fetchResult.status === 403) { + storeService.applicationStore && storeService.applicationStore.dispatch(new AddErrorInfoAction({ title: 'Forbidden', message:'Status: [403], access denied.' })); return { ...result, status: 403, - message: "Forbidden." + message: 'Forbidden.', }; - } - else if (fetchResult.status === 401) { - applicationStore && applicationStore.dispatch(new ReplaceAction(`/login?returnTo=${applicationStore.state.framework.navigationState.pathname}`)); + } else if (fetchResult.status === 401) { + storeService.applicationStore && storeService.applicationStore.dispatch(new ReplaceAction(`/login?returnTo=${storeService.applicationStore.state.framework.navigationState.pathname}`)); return { ...result, status: 401, - message: "Authentication requested by server." + message: 'Authentication requested by server.', }; } - const contentType = fetchResult.headers.get("Content-Type") || fetchResult.headers.get("content-type"); - const isJson = contentType && (contentType.toLowerCase().startsWith("application/json") || contentType.toLowerCase().startsWith("application/yang-data+json")); + const contentType = fetchResult.headers.get('Content-Type') || fetchResult.headers.get('content-type'); + const isJson = contentType && (contentType.toLowerCase().startsWith('application/json') || contentType.toLowerCase().startsWith('application/yang-data+json')); try { const data = (isJson ? await fetchResult.json() : await fetchResult.text()) as TData; return { ...result, status: fetchResult.status, message: fetchResult.statusText, - data: data + data: data, }; } catch (error) { return { ...result, status: fetchResult.status, message: error && error.message || String(error), - data: undefined + data: undefined, }; } } \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/services/settingsService.ts b/sdnr/wt/odlux/framework/src/services/settingsService.ts deleted file mode 100644 index 6633a794d..000000000 --- a/sdnr/wt/odlux/framework/src/services/settingsService.ts +++ /dev/null @@ -1,41 +0,0 @@ -/** - * ============LICENSE_START======================================================================== - * ONAP : ccsdk feature sdnr wt odlux - * ================================================================================================= - * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. - * ================================================================================================= - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 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 "./restService"; - - - const settingsPath ="/userdata"; - - - export function getSettings(partialPath?: string){ - let path = settingsPath; - if(partialPath){ - path+=partialPath - } - - const result = requestRest(path, {method: "GET"}) - return result; - } - - export function putSettings(partialPath: string, data: string){ - - const result = requestRest(settingsPath+partialPath, {method: "PUT", body: data}) - return result; - } - - diff --git a/sdnr/wt/odlux/framework/src/services/storeService.ts b/sdnr/wt/odlux/framework/src/services/storeService.ts new file mode 100644 index 000000000..cbb5987de --- /dev/null +++ b/sdnr/wt/odlux/framework/src/services/storeService.ts @@ -0,0 +1,11 @@ +import { ApplicationStore } from "../store/applicationStore"; + +let applicationStore: ApplicationStore | null = null; + +export const startSoreService = (store: ApplicationStore) => { + applicationStore = store; +}; + +export const storeService = { + get applicationStore() { return applicationStore; }, + }; \ No newline at end of file diff --git a/sdnr/wt/odlux/framework/src/services/userdataService.ts b/sdnr/wt/odlux/framework/src/services/userdataService.ts new file mode 100644 index 000000000..5c9b576c3 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/services/userdataService.ts @@ -0,0 +1,41 @@ +/** + * ============LICENSE_START======================================================================== + * ONAP : ccsdk feature sdnr wt odlux + * ================================================================================================= + * Copyright (C) 2021 highstreet technologies GmbH Intellectual Property. All rights reserved. + * ================================================================================================= + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 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 "./restService"; + + + const settingsPath ="/userdata"; + + + export function getUserdata(partialPath?: string){ + let path = settingsPath; + if(partialPath){ + path+=partialPath + } + + const result = requestRest(path, {method: "GET"}) + return result; + } + + export function saveUserdata(partialPath: string, data: string){ + + const result = requestRest(settingsPath+partialPath, {method: "PUT", body: data}) + return result; + } + + diff --git a/sdnr/wt/odlux/framework/src/store/applicationStore.ts b/sdnr/wt/odlux/framework/src/store/applicationStore.ts index a4545eff9..cbe8c20da 100644 --- a/sdnr/wt/odlux/framework/src/store/applicationStore.ts +++ b/sdnr/wt/odlux/framework/src/store/applicationStore.ts @@ -37,7 +37,7 @@ import { updatePolicies } from '../middleware/policies'; export type MiddlewareApi = MiddlewareArg; export interface IFrameworkStoreState { - applicationRegistraion: IApplicationRegistration; + applicationRegistration: IApplicationRegistration; applicationState: IApplicationState; authenticationState: IAuthenticationState; navigationState: INavigationState; @@ -48,7 +48,7 @@ export interface IApplicationStoreState { } const frameworkHandlers = combineActionHandler({ - applicationRegistraion: applicationRegistryHandler, + applicationRegistration: applicationRegistryHandler, applicationState: applicationStateHandler, authenticationState: authenticationStateHandler, navigationState: navigationStateHandler @@ -62,7 +62,7 @@ export const applicationStoreCreator = (): ApplicationStore => { const actionHandlers = Object.keys(applicationService.applications).reduce((acc, cur) => { const reg = applicationService.applications[cur]; reg && typeof reg.rootActionHandler === 'function' && (acc[cur] = reg.rootActionHandler); - reg && +(reg.middlewares || 0) && middlewares.push(...(reg.middlewares as Middleware[])); + reg && reg.middlewares && Array.isArray(reg.middlewares) && middlewares.push(...(reg.middlewares as Middleware[])); return acc; }, { framework: frameworkHandlers } as any); diff --git a/sdnr/wt/odlux/framework/src/utilities/logLevel.ts b/sdnr/wt/odlux/framework/src/utilities/logLevel.ts new file mode 100644 index 000000000..a198d98a9 --- /dev/null +++ b/sdnr/wt/odlux/framework/src/utilities/logLevel.ts @@ -0,0 +1,8 @@ +export enum LogLevel { + Always = 0, + Error = 1, + Warning = 2, + Info = 3, + Debug = 4, + Trace = 5, +} diff --git a/sdnr/wt/odlux/framework/src/views/about.tsx b/sdnr/wt/odlux/framework/src/views/about.tsx index ac219708d..937e74f33 100644 --- a/sdnr/wt/odlux/framework/src/views/about.tsx +++ b/sdnr/wt/odlux/framework/src/views/about.tsx @@ -15,22 +15,18 @@ * the License. * ============LICENSE_END========================================================================== */ -import * as React from 'react'; +import React, { FC, useEffect, useState } from 'react'; import * as marked from 'marked'; import * as hljs from 'highlight.js'; import { requestRestExt } from '../services/restService'; import { Button, Typography } from '@mui/material'; + const defaultRenderer = new marked.Renderer(); defaultRenderer.link = (href, title, text) => ( `${text}` ); -interface AboutState { - content: string | null; - isCopiedSuccessfully: boolean; - isContentLoadedSucessfully: boolean; -} -type odluxVersion= {version:string,build:string, framework: string, +type OdluxVersion= {version:string,build:string, framework: string, applications:{ configurationApp: string, connectApp: string, @@ -38,25 +34,27 @@ type odluxVersion= {version:string,build:string, framework: string, faultApp: string, helpApp: string, inventoryApp: string, + linkCalculationApp: string, maintenanceApp: string, mediatorApp: string, + networkMapApp: string, permanceHistoryApp: string }}; -type topologyVersion = {version: string, buildTimestamp: string}; - -class AboutComponent extends React.Component { - textarea: React.RefObject; +type TopologyVersion = {version: string, buildTimestamp: string}; +const AboutComponent: FC = (props) => { + + const textareaRef = React.createRef(); + const [content, setContent] = useState(null); + const [isCopiedSuccessfully, setCopySuccess] = useState(false); + const [isContetLoaded, setContentLoaded] = useState(false); - constructor(props: any) { - super(props); - this.state = { content: null, isCopiedSuccessfully:false, isContentLoadedSucessfully: false } - this.textarea = React.createRef(); - this.loadAboutContent(); - } + useEffect(()=>{ + loadAboutContent(); + },[]); - private getMarkOdluxVersionMarkdownTable(data:odluxVersion|null|undefined):string{ + const getMarkOdluxVersionMarkdownTable = (data:OdluxVersion|null|undefined):string => { if(!data) { return ""; }else{ @@ -72,6 +70,8 @@ class AboutComponent extends React.Component { `| InventoryApp | ${data.applications.inventoryApp}|\n `+ `| EventLogApp | ${data.applications.eventLogApp}|\n `+ `| MediatorApp | ${data.applications.mediatorApp}|\n `+ + `| NetworkMapApp | ${data.applications.networkMapApp}|\n `+ + `| LinkCalculatorApp | ${data.applications.linkCalculationApp}|\n `+ `| HelpApp | ${data.applications.helpApp}|\n `; } @@ -80,7 +80,7 @@ class AboutComponent extends React.Component { } } - private getTopologyVersionMarkdownTable(data: topologyVersion|null|undefined){ + const getTopologyVersionMarkdownTable = (data: TopologyVersion|null|undefined) => { if(!data){ return "No version"; } @@ -92,7 +92,7 @@ class AboutComponent extends React.Component { } } - private loadAboutContent(): void { + const loadAboutContent = (): void => { const baseUri = window.location.pathname.substring(0,window.location.pathname.lastIndexOf("/")+1); const init = { 'method': 'GET', @@ -102,7 +102,7 @@ class AboutComponent extends React.Component { } }; const p1 = requestRestExt('/about',init); - const p2 = requestRestExt(`${baseUri}version.json`); + const p2 = requestRestExt(`${baseUri}version.json`); const p3 = requestRestExt(`/topology/info/version`); Promise.all([p1,p2, p3]).then((responses) => { @@ -110,31 +110,30 @@ class AboutComponent extends React.Component { const response2 = responses[1]; const response3 = responses[2]; const content = response.status == 200 ? response.data : `${response.status} ${response.message}` || "Server error"; - const content2 = `\n## ODLUX Version Info\n`+(response2.status == 200 ? this.getMarkOdluxVersionMarkdownTable(response2.data) : `${response2.message}` || "ODLUX Server error"); - const content3 = `\n## Topology API Version Info\n`+(response3.status == 200 ? this.getTopologyVersionMarkdownTable(response3.data): `Topology API not available`); + const content2 = `\n## ODLUX Version Info\n`+(response2.status == 200 ? getMarkOdluxVersionMarkdownTable(response2.data) : `${response2.message}` || "ODLUX Server error"); + const content3 = `\n## Topology API Version Info\n`+(response3.status == 200 ? getTopologyVersionMarkdownTable(response3.data): `Topology API not available`); const loadedSucessfully = response.status == 200 ? true : false; - this.setState({ content: (content + content2 + content3 ) || null, isContentLoadedSucessfully: loadedSucessfully }); + setContent((content + content2 + content3 ) || null); + setContentLoaded(loadedSucessfully); }).catch((error) => { - this.setState({ content: error }) - }) + setContent(error); + }); } - copyToClipboard = (e: React.MouseEvent) =>{ + const copyToClipboard = (e: React.MouseEvent) =>{ e.preventDefault(); - if(this.textarea.current!==null){ - this.textarea.current.select(); + if(textareaRef.current!==null){ + textareaRef.current.select(); document.execCommand('copy'); if(e.currentTarget != null){ // refocus on button, otherwhise the textarea would be focused e.currentTarget.focus(); } - this.setState({isCopiedSuccessfully: true}); - window.setTimeout(()=>{this.setState({isCopiedSuccessfully: false});},2000); + setCopySuccess(true); + window.setTimeout(()=>{ setCopySuccess(false);},2000); } } - render() { - const markedOptions: marked.MarkedOptions = { gfm: true, breaks: false, @@ -157,17 +156,17 @@ class AboutComponent extends React.Component { const style: React.CSSProperties = {}; const containerStyle = { overflow: "auto", paddingRight: "20px" } - const html = (marked(this.state.content || 'loading', { renderer: markedOptions && markedOptions.renderer || defaultRenderer })); + const html = (marked(content || 'loading', { renderer: markedOptions && markedOptions.renderer || defaultRenderer })); return (
    - { this.state.isContentLoadedSucessfully && + { isContetLoaded &&
    - { - this.state.isCopiedSuccessfully && + isCopiedSuccessfully && copied successfully @@ -183,13 +182,12 @@ class AboutComponent extends React.Component {