aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--ui-react/package.json6
-rw-r--r--ui-react/src/LoopUI.js30
-rw-r--r--ui-react/src/OnapClamp.js2
-rw-r--r--ui-react/src/__test__/LoopCache.test.js217
-rw-r--r--ui-react/src/__test__/LoopCache_mokeLoopJsonCache.json117
-rw-r--r--ui-react/src/__test__/OpenLoopModal.test.js35
-rw-r--r--ui-react/src/api/LoopCache.js50
-rw-r--r--ui-react/src/api/LoopService.js56
-rw-r--r--ui-react/src/api/UserService.js25
-rw-r--r--ui-react/src/components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal.js66
-rw-r--r--ui-react/src/components/dialogs/LoopProperties.js112
-rw-r--r--ui-react/src/components/dialogs/OpenLoop/OpenLoopModal.js4
-rw-r--r--ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicyModal.js6
-rw-r--r--ui-react/src/components/dialogs/UserInfo.js161
-rw-r--r--ui-react/src/components/loop_viewer/logs/LoopLogs.js97
-rw-r--r--ui-react/src/components/loop_viewer/status/LoopStatus.js104
-rw-r--r--ui-react/src/components/loop_viewer/svg/LoopComponentConverter.js18
-rw-r--r--ui-react/src/components/loop_viewer/svg/LoopSvg.js57
-rw-r--r--ui-react/src/components/menu/MenuBar.js5
-rw-r--r--ui-react/src/setupTests.js4
-rw-r--r--ui-react/src/theme/globalStyle.js19
22 files changed, 1067 insertions, 125 deletions
diff --git a/.gitignore b/.gitignore
index b6d49c75b..fbdcf4af5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,7 +8,6 @@ target
ui-react/node_modules
ui-react/build
**/package-lock.json
-**/logs/
**/.evosuite/
**/debug-logs/
*.log
diff --git a/ui-react/package.json b/ui-react/package.json
index f1ae2bc9d..de7cb26d1 100644
--- a/ui-react/package.json
+++ b/ui-react/package.json
@@ -28,5 +28,9 @@
"not dead",
"not ie <= 11",
"not op_mini all"
- ]
+ ],
+ "devDependencies": {
+ "enzyme": "3.10.0",
+ "enzyme-adapter-react-16": "1.14.0"
+ }
}
diff --git a/ui-react/src/LoopUI.js b/ui-react/src/LoopUI.js
index a1aff3d61..7d8fcb5cf 100644
--- a/ui-react/src/LoopUI.js
+++ b/ui-react/src/LoopUI.js
@@ -34,10 +34,12 @@ import LoopStatus from './components/loop_viewer/status/LoopStatus';
import UserService from './api/UserService';
import LoopCache from './api/LoopCache';
-import { Route } from 'react-router-dom'
+import { Route, Redirect } from 'react-router-dom'
import OpenLoopModal from './components/dialogs/OpenLoop/OpenLoopModal';
import OperationalPolicyModal from './components/dialogs/OperationalPolicy/OperationalPolicyModal';
import ConfigurationPolicyModal from './components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal';
+import LoopProperties from './components/dialogs/LoopProperties';
+import UserInfo from './components/dialogs/UserInfo';
const ProjectNameStyled = styled.a`
vertical-align: middle;
@@ -46,7 +48,7 @@ const ProjectNameStyled = styled.a`
`
const LoopViewDivStyled = styled.div`
- height: 90vh;
+ height: 100%;
overflow: hidden;
margin-left: 10px;
margin-right: 10px;
@@ -78,9 +80,11 @@ const LoopViewLoopNameSpanStyled = styled.span`
export default class LoopUI extends React.Component {
+ static defaultLoopName="Empty (NO loop loaded yet)";
+
state = {
userName: null,
- loopName: "Empty (NO loop loaded yet)",
+ loopName: LoopUI.defaultLoopName,
loopCache: new LoopCache({}),
};
@@ -90,7 +94,7 @@ export default class LoopUI extends React.Component {
this.updateLoopCache = this.updateLoopCache.bind(this);
}
- componentDidMount() {
+ componentWillMount() {
this.getUser();
}
@@ -102,7 +106,7 @@ export default class LoopUI extends React.Component {
renderMenuNavBar() {
return (
- <MenuBar />
+ <MenuBar loopCache={this.state.loopCache}/>
);
}
@@ -136,7 +140,7 @@ export default class LoopUI extends React.Component {
renderLoopViewHeader() {
return (
<LoopViewHeaderDivStyled>
- Loop Viewer - <LoopViewLoopNameSpanStyled id="loop_name">{this.state.loopName}</LoopViewLoopNameSpanStyled>
+ Loop Viewer - {this.state.loopName}
</LoopViewHeaderDivStyled>
);
}
@@ -145,12 +149,16 @@ export default class LoopUI extends React.Component {
return (
<LoopViewBodyDivStyled>
<LoopSvg loopCache={this.state.loopCache} />
- <LoopLogs />
- <LoopStatus />
+ <LoopStatus loopCache={this.state.loopCache}/>
+ <LoopLogs loopCache={this.state.loopCache} />
</LoopViewBodyDivStyled>
);
}
+ getLoopCache() {
+ return this.state.loopCache;
+
+ }
renderLoopViewer() {
return (
<LoopViewDivStyled>
@@ -162,6 +170,7 @@ export default class LoopUI extends React.Component {
updateLoopCache(loopJson) {
this.setState({ loopCache: new LoopCache(loopJson) });
+ this.setState({ loopName: this.state.loopCache.getLoopName() });
}
render() {
@@ -172,8 +181,11 @@ export default class LoopUI extends React.Component {
{this.renderLoopViewer()}
<Route path="/operationalPolicyModal"
render={(routeProps) => (<OperationalPolicyModal {...routeProps} loopCache={this.state.loopCache} />)} />
- <Route path="/configurationPolicyModal" render={(routeProps) => (<ConfigurationPolicyModal {...routeProps} loopCache={this.state.loopCache} />)} />
+ <Route path="/configurationPolicyModal/:componentName" render={(routeProps) => (<ConfigurationPolicyModal {...routeProps} loopCache={this.state.loopCache} />)} />
<Route path="/openLoop" render={(routeProps) => (<OpenLoopModal {...routeProps} updateLoopCacheFunction={this.updateLoopCache} />)} />
+ <Route path="/loopProperties" render={(routeProps) => (<LoopProperties {...routeProps} loopCache={this.getLoopCache()} />)} />
+ <Route path="/userInfo" render={(routeProps) => (<UserInfo {...routeProps} />)} />
+ <Route path="/closeLoop" render={(routeProps) => (<Redirect to='/'/>)} />
</div>
);
}
diff --git a/ui-react/src/OnapClamp.js b/ui-react/src/OnapClamp.js
index a8cc2154a..506f6e09d 100644
--- a/ui-react/src/OnapClamp.js
+++ b/ui-react/src/OnapClamp.js
@@ -29,7 +29,7 @@ import { DefaultClampTheme } from './theme/globalStyle.js';
export default class OnapClamp extends LoopUI {
render() {
- console.log("Onap Clamp UI starting");
+ console.info("Onap Clamp UI starting");
return (
<ThemeProvider theme={DefaultClampTheme}>
{super.render()}
diff --git a/ui-react/src/__test__/LoopCache.test.js b/ui-react/src/__test__/LoopCache.test.js
new file mode 100644
index 000000000..e5b50259d
--- /dev/null
+++ b/ui-react/src/__test__/LoopCache.test.js
@@ -0,0 +1,217 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights
+ * reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+import React from 'react';
+import LoopCache from '../api/LoopCache';
+
+const json = require('./LoopCache_mokeLoopJsonCache.json');
+
+describe('Verify LoopCache functions', () => {
+ const loopCache = new LoopCache(json);
+ it('getLoopName', () => {
+ expect(loopCache.getLoopName()).toBe("LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca");
+ });
+
+ it('getOperationalPolicyConfigurationJson', () => {
+ const opPolicyConfig = {
+ "guard_policies": {},
+ "operational_policy": {
+ "controlLoop": {},
+ "policies": []
+ }
+ };
+ expect(loopCache.getOperationalPolicyConfigurationJson()).toStrictEqual(opPolicyConfig);
+ });
+
+ it('getOperationalPolicies', () => {
+ const opPolicy = [{
+ "name": "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "configurationsJson": {
+ "guard_policies": {},
+ "operational_policy": {
+ "controlLoop": {},
+ "policies": []
+
+ }
+ }
+ }];
+ expect(loopCache.getOperationalPolicies()).toStrictEqual(opPolicy);
+ });
+
+ it('getGlobalProperties', () => {
+ const globelProp = {
+ "dcaeDeployParameters": {
+ "location_id": "",
+ "service_id": "",
+ "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+ }
+ };
+ expect(loopCache.getGlobalProperties()).toStrictEqual(globelProp);
+ });
+
+ it('getDcaeDeploymentProperties', () => {
+ const deploymentProp = {
+ "location_id": "",
+ "service_id": "",
+ "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+ };
+ expect(loopCache.getDcaeDeploymentProperties()).toStrictEqual(deploymentProp);
+ });
+
+ it('getMicroServicesJsonForType', () => {
+ const msJson = {
+ "name": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "modelType": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+ "properties": {"domain": "measurementsForVfScaling"},
+ "shared": false,
+ "jsonRepresentation": {"schema": {}}
+ };
+ expect(loopCache.getMicroServicesJsonForType("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(msJson);
+ expect(loopCache.getMicroServicesJsonForType("TCA_h2NMX_v1_0_ResourceInstanceName1_tca_2")).toBeNull();
+ });
+
+ it('getMicroServiceProperties', () => {
+ const msProp = {"domain": "measurementsForVfScaling"};
+ expect(loopCache.getMicroServiceProperties("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(msProp);
+ expect(loopCache.getMicroServiceProperties("TCA_h2NMX_v1_0_ResourceInstanceName1_tca_2")).toBeNull();
+ });
+
+ it('getMicroServiceJsonRepresentationForType', () => {
+ const msJsonRepresentation = {"schema": {}};
+ expect(loopCache.getMicroServiceJsonRepresentationForType("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(msJsonRepresentation);
+ });
+
+ it('getMicroServiceJsonRepresentationForType', () => {
+ const msJsonRepresentation = {"schema": {}};
+ expect(loopCache.getMicroServiceJsonRepresentationForType("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(msJsonRepresentation);
+ });
+
+ it('getResourceDetailsVfProperty', () => {
+ const resourceVF = {
+ "vLoadBalancerMS 0": {
+ "resourceVendor": "Test",
+ "resourceVendorModelNumber": "",
+ "name": "vLoadBalancerMS",
+ "description": "vLBMS",
+ "invariantUUID": "1a31b9f2-e50d-43b7-89b3-a040250cf506",
+ "subcategory": "Load Balancer",
+ "category": "Application L4+",
+ "type": "VF",
+ "UUID": "b4c4f3d7-929e-4b6d-a1cd-57e952ddc3e6",
+ "version": "1.0",
+ "resourceVendorRelease": "1.0",
+ "customizationUUID": "465246dc-7748-45f4-a013-308d92922552"
+ }
+ };
+ expect(loopCache.getResourceDetailsVfProperty()).toStrictEqual(resourceVF);
+ });
+
+ it('getResourceDetailsVfModuleProperty', () => {
+ const vfModule = {
+ "Vloadbalancerms..vpkg..module-1": {
+ "vfModuleModelInvariantUUID": "ca052563-eb92-4b5b-ad41-9111768ce043",
+ "vfModuleModelVersion": "1",
+ "vfModuleModelName": "Vloadbalancerms..vpkg..module-1",
+ "vfModuleModelUUID": "1e725ccc-b823-4f67-82b9-4f4367070dbc",
+ "vfModuleModelCustomizationUUID": "1bffdc31-a37d-4dee-b65c-dde623a76e52",
+ "min_vf_module_instances": 0,
+ "vf_module_label": "vpkg",
+ "max_vf_module_instances": 1,
+ "vf_module_type": "Expansion",
+ "isBase": false,
+ "initial_count": 0,
+ "volume_group": false
+ }
+ };
+ expect(loopCache.getResourceDetailsVfModuleProperty()).toStrictEqual(vfModule);
+ });
+
+ it('getLoopLogsArray', () => {
+ const logs = [
+ {
+ "id": 1,
+ "logType": "INFO",
+ "logComponent": "CLAMP",
+ "message": "Operational and Guard policies UPDATED",
+ "logInstant": "2019-07-08T09:44:37Z"
+ }
+ ];
+ expect(loopCache.getLoopLogsArray()).toStrictEqual(logs);
+ });
+
+ it('getComponentStates', () => {
+ const component = {
+ "POLICY": {
+ "componentState": {
+ "stateName": "NOT_SENT",
+ "description": "The policies defined have NOT yet been created on the policy engine"
+ }
+ },
+ "DCAE": {
+ "componentState": {
+ "stateName": "BLUEPRINT_DEPLOYED",
+ "description": "The DCAE blueprint has been found in the DCAE inventory but not yet instancianted for this loop"
+ }
+ }
+ };
+ expect(loopCache.getComponentStates()).toStrictEqual(component);
+ });
+
+ it('updateGlobalProperties', () => {
+ const newGlobalProps = {
+ "dcaeDeployParameters": {
+ "location_id": "newLocation",
+ "service_id": "newServiceId",
+ "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca_2"
+ }
+ };
+ loopCache.updateGlobalProperties(newGlobalProps);
+ expect(loopCache.getGlobalProperties()).toStrictEqual(newGlobalProps);
+ });
+
+ it('updateOperationalPolicyProperties', () => {
+ const newOpPolicy = [{
+ "name": "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca_new",
+ "configurationsJson": {
+ "guard_policies": {},
+ "operational_policy": {
+ "controlLoop": {},
+ "policies": []
+ }
+ }
+ }];
+ loopCache.updateOperationalPolicyProperties(newOpPolicy);
+ expect(loopCache.getOperationalPolicies()).toStrictEqual(newOpPolicy);
+ });
+
+ it('updateMicroServiceProperties', () => {
+ const newMsPolicy = {
+ "name": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "modelType": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+ "properties": {"domain": "measurementsForVfScalingNew"},
+ "shared": true,
+ "jsonRepresentation": {"schema": {}}
+ };;
+ loopCache.updateMicroServiceProperties("TCA_h2NMX_v1_0_ResourceInstanceName1_tca", newMsPolicy);
+ expect(loopCache.getMicroServicesJsonForType("TCA_h2NMX_v1_0_ResourceInstanceName1_tca")).toStrictEqual(newMsPolicy);
+ });
+ });
diff --git a/ui-react/src/__test__/LoopCache_mokeLoopJsonCache.json b/ui-react/src/__test__/LoopCache_mokeLoopJsonCache.json
new file mode 100644
index 000000000..184eaf7cd
--- /dev/null
+++ b/ui-react/src/__test__/LoopCache_mokeLoopJsonCache.json
@@ -0,0 +1,117 @@
+{
+ "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+ "dcaeBlueprintId": "typeId-3a942643-a8f7-4e54-b2c1-eea8daba2b17",
+ "globalPropertiesJson": {
+ "dcaeDeployParameters": {
+ "location_id": "",
+ "service_id": "",
+ "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+ }
+ },
+ "modelPropertiesJson": {
+ "serviceDetails": {
+ "serviceType": "",
+ "namingPolicy": "",
+ "environmentContext": "General_Revenue-Bearing",
+ "serviceEcompNaming": "true",
+ "serviceRole": "",
+ "name": "vLoadBalancerMS",
+ "description": "vLBMS",
+ "invariantUUID": "30ec5b59-4799-48d8-ac5f-1058a6b0e48f",
+ "ecompGeneratedNaming": "true",
+ "category": "Network L4+",
+ "type": "Service",
+ "UUID": "63cac700-ab9a-4115-a74f-7eac85e3fce0",
+ "instantiationType": "A-la-carte"
+ },
+ "resourceDetails": {
+ "CP": {},
+ "VL": {},
+ "VF": {
+ "vLoadBalancerMS 0": {
+ "resourceVendor": "Test",
+ "resourceVendorModelNumber": "",
+ "name": "vLoadBalancerMS",
+ "description": "vLBMS",
+ "invariantUUID": "1a31b9f2-e50d-43b7-89b3-a040250cf506",
+ "subcategory": "Load Balancer",
+ "category": "Application L4+",
+ "type": "VF",
+ "UUID": "b4c4f3d7-929e-4b6d-a1cd-57e952ddc3e6",
+ "version": "1.0",
+ "resourceVendorRelease": "1.0",
+ "customizationUUID": "465246dc-7748-45f4-a013-308d92922552"
+ }
+ },
+ "CR": {},
+ "VFC": {},
+ "PNF": {},
+ "Service": {},
+ "CVFC": {},
+ "Service Proxy": {},
+ "Configuration": {},
+ "AllottedResource": {},
+ "VFModule": {
+ "Vloadbalancerms..vpkg..module-1": {
+ "vfModuleModelInvariantUUID": "ca052563-eb92-4b5b-ad41-9111768ce043",
+ "vfModuleModelVersion": "1",
+ "vfModuleModelName": "Vloadbalancerms..vpkg..module-1",
+ "vfModuleModelUUID": "1e725ccc-b823-4f67-82b9-4f4367070dbc",
+ "vfModuleModelCustomizationUUID": "1bffdc31-a37d-4dee-b65c-dde623a76e52",
+ "min_vf_module_instances": 0,
+ "vf_module_label": "vpkg",
+ "max_vf_module_instances": 1,
+ "vf_module_type": "Expansion",
+ "isBase": false,
+ "initial_count": 0,
+ "volume_group": false
+ }
+ }
+ }
+ },
+ "lastComputedState": "DESIGN",
+ "components": {
+ "POLICY": {
+ "componentState": {
+ "stateName": "NOT_SENT",
+ "description": "The policies defined have NOT yet been created on the policy engine"
+ }
+ },
+ "DCAE": {
+ "componentState": {
+ "stateName": "BLUEPRINT_DEPLOYED",
+ "description": "The DCAE blueprint has been found in the DCAE inventory but not yet instancianted for this loop"
+ }
+ }
+ },
+ "operationalPolicies": [
+ {
+ "name": "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "configurationsJson": {
+ "guard_policies": {},
+ "operational_policy": {
+ "controlLoop": {},
+ "policies": []
+ }
+ }
+ }
+ ],
+ "microServicePolicies": [
+ {
+ "name": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "modelType": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+ "properties": {"domain": "measurementsForVfScaling"},
+ "shared": false,
+ "jsonRepresentation": {"schema": {}}
+ }
+ ],
+ "loopLogs": [
+ {
+ "id": 1,
+ "logType": "INFO",
+ "logComponent": "CLAMP",
+ "message": "Operational and Guard policies UPDATED",
+ "logInstant": "2019-07-08T09:44:37Z"
+ }
+ ]
+}
diff --git a/ui-react/src/__test__/OpenLoopModal.test.js b/ui-react/src/__test__/OpenLoopModal.test.js
new file mode 100644
index 000000000..044eeda89
--- /dev/null
+++ b/ui-react/src/__test__/OpenLoopModal.test.js
@@ -0,0 +1,35 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights
+ * reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+import React from 'react';
+import { shallow } from 'enzyme';
+import OpenLoopModal from '../components/dialogs/OpenLoop/OpenLoopModal';
+
+describe('Verify OpenLoopModal', () => {
+
+ it('Test the render method', () => {
+ const component = shallow(<OpenLoopModal/>);
+ expect(component).toMatchSnapshot();
+ });
+
+
+});
diff --git a/ui-react/src/api/LoopCache.js b/ui-react/src/api/LoopCache.js
index 4c8f68c23..3ee5acc68 100644
--- a/ui-react/src/api/LoopCache.js
+++ b/ui-react/src/api/LoopCache.js
@@ -29,13 +29,11 @@ export default class LoopCache {
}
updateMicroServiceProperties(type, newMsProperties) {
- if (newMsProperties["name"] === type) {
for (var policy in this.loopJsonCache["microServicePolicies"]) {
if (this.loopJsonCache["microServicePolicies"][policy]["name"] === type) {
- this.loopJsonCache["microServicePolicies"][policy] = newMsProperties;
+ this.loopJsonCache["microServicePolicies"][policy]["properties"] = newMsProperties;
}
}
- }
}
updateGlobalProperties(newGlobalProperties) {
@@ -51,49 +49,47 @@ export default class LoopCache {
}
getOperationalPolicyConfigurationJson() {
- return JSON.parse(JSON.stringify(this.loopJsonCache["operationalPolicies"]["0"]["configurationsJson"]));
+ return this.loopJsonCache["operationalPolicies"]["0"]["configurationsJson"];
}
getOperationalPolicies() {
- return JSON.parse(JSON.stringify(this.loopJsonCache["operationalPolicies"]));
+ return this.loopJsonCache["operationalPolicies"];
}
getGlobalProperties() {
- return JSON.parse(JSON.stringify(this.loopJsonCache["globalPropertiesJson"]));
+ return this.loopJsonCache["globalPropertiesJson"];
}
getDcaeDeploymentProperties() {
- return JSON.parse(JSON.stringify(this.loopJsonCache["globalPropertiesJson"]["dcaeDeployParameters"]));
+ return this.loopJsonCache["globalPropertiesJson"]["dcaeDeployParameters"];
+ }
+
+ getMicroServicePolicies() {
+ return this.loopJsonCache["microServicePolicies"];
}
- getMicroServicesJsonForType(type) {
- var msProperties = this.loopJsonCache["microServicePolicies"];
+ getMicroServiceForName(name) {
+ var msProperties=this.getMicroServicePolicies();
for (var policy in msProperties) {
- if (msProperties[policy]["name"] === type) {
- return JSON.parse(JSON.stringify(msProperties[policy]));
+ if (msProperties[policy]["name"] === name) {
+ return msProperties[policy];
}
}
return null;
}
- getMicroServiceProperties(type) {
- var msProperties = this.loopJsonCache["microServicePolicies"];
- for (var policy in msProperties) {
- if (msProperties[policy]["name"] === type) {
- if (msProperties[policy]["properties"] !== null && msProperties[policy]["properties"] !== undefined) {
- return JSON.parse(JSON.stringify(msProperties[policy]["properties"]));
- }
- }
+ getMicroServicePropertiesForName(name) {
+ var msConfig = this.getMicroServiceForName(name);
+ if (msConfig !== null) {
+ return msConfig["properties"];
}
return null;
}
- getMicroServiceJsonRepresentationForType(type) {
- var msProperties = this.loopJsonCache["microServicePolicies"];
- for (var policy in msProperties) {
- if (msProperties[policy]["name"] === type) {
- return JSON.parse(JSON.stringify(msProperties[policy]["jsonRepresentation"]));
- }
+ getMicroServiceJsonRepresentationForName(name) {
+ var msConfig = this.getMicroServiceForName(name);
+ if (msConfig !== null) {
+ return msConfig["jsonRepresentation"];
}
return null;
}
@@ -110,6 +106,10 @@ export default class LoopCache {
return this.loopJsonCache.loopLogs;
}
+ getComputedState() {
+ return this.loopJsonCache.lastComputedState;
+ }
+
getComponentStates() {
return this.loopJsonCache.components;
}
diff --git a/ui-react/src/api/LoopService.js b/ui-react/src/api/LoopService.js
index 2813a7c08..031ec638f 100644
--- a/ui-react/src/api/LoopService.js
+++ b/ui-react/src/api/LoopService.js
@@ -22,7 +22,7 @@
export default class LoopService {
static getLoopNames() {
- return fetch('/restservices/clds/v2/loop/getAllNames', { method: 'GET', credentials: 'include', })
+ return fetch('/restservices/clds/v2/loop/getAllNames', { method: 'GET', credentials: 'same-origin', })
.then(function (response) {
console.debug("GetLoopNames response received: ", response.status);
if (response.ok) {
@@ -42,9 +42,9 @@ export default class LoopService {
return fetch('/restservices/clds/v2/loop/' + loopName, {
method: 'GET',
headers: {
- "Content-Type": "application/json"
+ "Content-Type": "application/json",
},
- credentials: 'include',
+ credentials: 'same-origin',
})
.then(function (response) {
console.debug("GetLoop response received: ", response.status);
@@ -64,7 +64,7 @@ export default class LoopService {
static getSvg(loopName) {
return fetch('/restservices/clds/v2/loop/svgRepresentation/' + loopName, {
method: 'GET',
- credentials: 'include',
+ credentials: 'same-origin',
})
.then(function (response) {
console.debug("svgRepresentation response received: ", response.status);
@@ -80,4 +80,52 @@ export default class LoopService {
return "";
});
}
+
+ static setMicroServiceProperties(loopName, jsonData) {
+ return fetch('/restservices/clds/v2/loop/updateMicroservicePolicy/' + loopName, {
+ method: 'POST',
+ credentials: 'same-origin',
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(jsonData),
+ })
+ .then(function (response) {
+ console.debug("updateMicroservicePolicy response received: ", response.status);
+ if (response.ok) {
+ return response.text();
+ } else {
+ console.error("updateMicroservicePolicy query failed");
+ return "";
+ }
+ })
+ .catch(function (error) {
+ console.error("updateMicroservicePolicy error received", error);
+ return "";
+ });
+ }
+
+ static updateGlobalProperties(loopName, jsonData) {
+ return fetch('/restservices/clds/v2/loop/updateGlobalProperties/' + loopName, {
+ method: 'POST',
+ credentials: 'same-origin',
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify(jsonData),
+ })
+ .then(function (response) {
+ console.debug("updateGlobalProperties response received: ", response.status);
+ if (response.ok) {
+ return response.text();
+ } else {
+ console.error("updateGlobalProperties query failed");
+ return "";
+ }
+ })
+ .catch(function (error) {
+ console.error("updateGlobalProperties error received", error);
+ return "";
+ });
+ }
}
diff --git a/ui-react/src/api/UserService.js b/ui-react/src/api/UserService.js
index 8f53d7b8a..be21e692a 100644
--- a/ui-react/src/api/UserService.js
+++ b/ui-react/src/api/UserService.js
@@ -26,7 +26,7 @@ export default class UserService {
static login() {
return fetch('/restservices/clds/v1/user/getUser', {
method: 'GET',
- credentials: 'include',
+ credentials: 'same-origin',
})
.then(function (response) {
console.debug("getUser response received, status code:", response.status);
@@ -47,5 +47,26 @@ export default class UserService {
return UserService.notLoggedUserName;
});
}
-}
+ static getUserInfo() {
+ return fetch('/restservices/clds/v1/clds/cldsInfo', {
+ method: 'GET',
+ credentials: 'same-origin',
+ })
+ .then(function (response) {
+ console.debug("getUserInfo response received, status code:", response.status);
+ if (response.ok) {
+ return response.json();
+ }
+ })
+ .then(function (data) {
+ console.info ("User info received:",data)
+ return data;
+ })
+ .catch(function(error) {
+ console.warn("getUserInfo error received, user set to: ",UserService.notLoggedUserName);
+ console.error("getUserInfo error:",error);
+ return;
+ });
+ }
+}
diff --git a/ui-react/src/components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal.js b/ui-react/src/components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal.js
index 1a8b6e2c5..8178bf47f 100644
--- a/ui-react/src/components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal.js
+++ b/ui-react/src/components/dialogs/ConfigurationPolicy/ConfigurationPolicyModal.js
@@ -24,8 +24,9 @@
import React from 'react'
import Button from 'react-bootstrap/Button';
import Modal from 'react-bootstrap/Modal';
-import { LOOP_CACHE } from '../../../api/LoopCache'
import styled from 'styled-components';
+import LoopService from '../../../api/LoopService';
+import JSONEditor from '@json-editor/json-editor';
const ModalStyled = styled(Modal)`
background-color: transparent;
@@ -33,23 +34,71 @@ const ModalStyled = styled(Modal)`
export default class ConfigurationPolicyModal extends React.Component {
+ state = {
+ show: true,
+ loopCache: this.props.loopCache,
+ jsonEditor: null,
+ componentName: "",
+ };
+
constructor(props, context) {
super(props, context);
-
this.handleClose = this.handleClose.bind(this);
+ this.handleSave = this.handleSave.bind(this);
+ this.renderJsonEditor = this.renderJsonEditor.bind(this);
+ this.state.componentName = props.match.params.componentName;
+ }
- this.state = {
- show: true,
- };
+ handleSave() {
+ var errors = this.state.jsonEditor.validate();
+ var editorData = this.state.jsonEditor.getValue();
+
+ if (errors.length !== 0) {
+ console.error("Errors detected during config policy data validation ", errors);
+ }
+ else {
+ console.info("NO validation errors found in config policy data");
+ this.state.loopCache.updateMicroServiceProperties(this.state.componentName, editorData[0]);
+ LoopService.setMicroServiceProperties(this.state.loopCache.getLoopName(), this.state.loopCache.getMicroServiceForName(this.state.componentName));
+ }
+
+ this.setState({ show: false });
+ this.props.history.push('/');
}
handleClose() {
this.setState({ show: false });
- this.props.history.push('/')
+ this.props.history.push('/');
}
+ componentDidMount() {
+ this.renderJsonEditor();
+ }
+
+ renderJsonEditor() {
+ console.debug("Rendering ConfigurationPolicyModal ", this.state.componentName);
+ var toscaModel = this.state.loopCache.getMicroServiceJsonRepresentationForName(this.state.componentName);
+ if (toscaModel == null) {
+ return;
+ }
+ var editorData = this.state.loopCache.getMicroServicePropertiesForName(this.state.componentName);
+
+ JSONEditor.defaults.options.theme = 'bootstrap4';
+ //JSONEditor.defaults.options.iconlib = 'bootstrap2';
+ JSONEditor.defaults.options.object_layout = 'grid';
+ JSONEditor.defaults.options.disable_properties = true;
+ JSONEditor.defaults.options.disable_edit_json = false;
+ JSONEditor.defaults.options.disable_array_reorder = true;
+ JSONEditor.defaults.options.disable_array_delete_last_row = true;
+ JSONEditor.defaults.options.disable_array_delete_all_rows = false;
+ JSONEditor.defaults.options.show_errors = 'always';
+ this.setState({
+ jsonEditor: new JSONEditor(document.getElementById("editor"),
+ { schema: toscaModel.schema, startval: editorData })
+ })
+ }
render() {
return (
@@ -58,15 +107,14 @@ export default class ConfigurationPolicyModal extends React.Component {
<Modal.Title>Configuration policies</Modal.Title>
</Modal.Header>
<Modal.Body>
-
-
+ <div id="editor" />
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={this.handleClose}>
Close
</Button>
- <Button variant="primary" onClick={this.handleClose}>
+ <Button variant="primary" onClick={this.handleSave}>
Save Changes
</Button>
</Modal.Footer>
diff --git a/ui-react/src/components/dialogs/LoopProperties.js b/ui-react/src/components/dialogs/LoopProperties.js
new file mode 100644
index 000000000..2cde8d46b
--- /dev/null
+++ b/ui-react/src/components/dialogs/LoopProperties.js
@@ -0,0 +1,112 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+
+import React from 'react'
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import Form from 'react-bootstrap/Form';
+import styled from 'styled-components';
+import LoopService from '../../api/LoopService';
+
+const ModalStyled = styled(Modal)`
+ background-color: transparent;
+`
+
+export default class LoopProperties extends React.Component {
+
+ formProps = {dcaeDeployParameters: {
+ "location_id": "",
+ "service_id": "",
+ "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+ }};
+
+ constructor(props, context) {
+ super(props, context);
+
+ this.handleClose = this.handleClose.bind(this);
+ this.handleChange = this.handleChange.bind(this);
+ this.saveProperties = this.saveProperties.bind(this);
+ this.initialValues = this.initialValues.bind(this);
+ this.state = {
+ show: true,
+ loopName: 'LOOP_h2NMX_v1_0_ResourceInstanceName1_tca',
+ form: this.formProps
+ };
+
+ }
+ initialValues() {
+ const updatedForm = this.state.form;
+ Object.keys(updatedForm).forEach((key) => {
+ if (key === 'dcaeDeployParameters') {
+ updatedForm[key] = JSON.stringify(this.state.form[key]);
+ } else {
+ updatedForm[key] = this.state.form[key];
+ }
+ this.setState({ form: updatedForm });
+ });
+ }
+ handleClose() {
+ this.props.history.push('/');
+ }
+ handleChange(e) {
+ const formUpdated = this.state.form;
+ formUpdated[e.target.name] = e.target.value;
+ this.setState({ form: formUpdated });
+ };
+ saveProperties(event) {
+ // translate the deploymentParameter into jsonFormat at submit time
+ const updatedForm = this.state.form;
+ Object.keys(updatedForm).forEach((key) => {
+ if (key === 'dcaeDeployParameters') {
+ try {
+ let value = JSON.parse(updatedForm[key]);
+ updatedForm[key] = value;
+ // save Properties
+ this.setState( {form: updatedForm} );
+ LoopService.updateGlobalProperties(this.state.loopName, this.state.form);
+ this.props.history.push('/');
+ } catch (error) {
+ alert("Deployment Parameters is not in good Json format. Please correct it.");
+ }
+ }
+ });
+ }
+ render() {
+ return (
+ <ModalStyled size="lg" show={this.state.show} onHide={this.handleClose} onEntered={this.initialValues}>
+ <Modal.Header closeButton>
+ <Modal.Title>Model Properties</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form.Group controlId="exampleForm.ControlTextarea1">
+ <Form.Label>Deploy Parameters</Form.Label>
+ <Form.Control as="textarea" rows="3" name="dcaeDeployParameters" onChange={this.handleChange} defaultValue={this.state.form["dcaeDeployParameters"]}/>
+ </Form.Group>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button variant="secondary" type="null" onClick={this.handleClose}>Cacel</Button>
+ <Button variant="primary" type="submit" onClick={this.saveProperties}>Save</Button>
+ </Modal.Footer>
+ </ModalStyled>
+ );
+ }
+}
diff --git a/ui-react/src/components/dialogs/OpenLoop/OpenLoopModal.js b/ui-react/src/components/dialogs/OpenLoop/OpenLoopModal.js
index 6986209d4..0bf71580a 100644
--- a/ui-react/src/components/dialogs/OpenLoop/OpenLoopModal.js
+++ b/ui-react/src/components/dialogs/OpenLoop/OpenLoopModal.js
@@ -54,7 +54,7 @@ export default class OpenLoopModal extends React.Component {
};
}
- componentDidMount() {
+ componentWillMount() {
this.getLoopNames();
}
@@ -110,7 +110,7 @@ export default class OpenLoopModal extends React.Component {
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" type="null" onClick={this.handleClose}>Cancel</Button>
- <Button variant="primary" type="submit" onClick={this.handleOpen}>OK</Button>
+ <Button variant="primary" type="submit" onClick={this.handleOpen}>Open</Button>
</Modal.Footer>
</ModalStyled>
diff --git a/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicyModal.js b/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicyModal.js
index 6822f3ff3..2a812c877 100644
--- a/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicyModal.js
+++ b/ui-react/src/components/dialogs/OperationalPolicy/OperationalPolicyModal.js
@@ -217,7 +217,7 @@ export default class OperationalPolicyModal extends React.Component {
</div>
<span id="formSpan" style={{ display: 'none' }}>
- <form className="policyProperties" className="form-horizontal"
+ <form className="policyProperties form-horizontal"
style={{ border: '2px dotted gray' }}
title="Operational Policy Properties">
<div className="form-group clearfix">
@@ -350,7 +350,7 @@ export default class OperationalPolicyModal extends React.Component {
</div>
</div>
</form>
- <form className="policyTarget" className="form-horizontal"
+ <form className="policyTarget form-horizontal"
title="Operational Policy Target" style={{ border: '2px dotted gray' }}>
<div className="form-group clearfix">
<label htmlFor="type" className="col-sm-4 control-label"> Target
@@ -437,7 +437,7 @@ export default class OperationalPolicyModal extends React.Component {
</select>
</div>
</div>
- <form className="guardProperties" className="form-horizontal"
+ <form className="guardProperties form-horizontal"
title="Guard policy associated" style={{ border: '2px dotted gray' }}>
<div className="form-group clearfix withnote">
diff --git a/ui-react/src/components/dialogs/UserInfo.js b/ui-react/src/components/dialogs/UserInfo.js
new file mode 100644
index 000000000..a8ef717b3
--- /dev/null
+++ b/ui-react/src/components/dialogs/UserInfo.js
@@ -0,0 +1,161 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+
+import React from 'react'
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import Form from 'react-bootstrap/Form';
+import Row from 'react-bootstrap/Row';
+import Col from 'react-bootstrap/Col';
+import styled from 'styled-components';
+import UserService from '../../api/UserService';
+
+const ModalStyled = styled(Modal)`
+ background-color: transparent;
+`
+
+export default class UserInfo extends React.Component {
+
+ constructor(props, context) {
+ super(props, context);
+
+ this.handleClose = this.handleClose.bind(this);
+ this.initialValues = this.initialValues.bind(this);
+ this.renderReadTemplatePermission = this.renderReadTemplatePermission.bind(this);
+ this.renderReadModelPermission = this.renderReadModelPermission.bind(this);
+ this.renderReadToscaPermission = this.renderReadToscaPermission.bind(this);
+ this.renderUpdateTemplatePermission = this.renderUpdateTemplatePermission.bind(this);
+ this.renderUpdateModelPermission = this.renderUpdateModelPermission.bind(this);
+ this.renderUpdateToscaPermission = this.renderUpdateToscaPermission.bind(this);
+ this.renderUserName = this.renderUserName.bind(this);
+ this.state = {
+ show: true,
+ userInfo: {permissionReadTemplate: true,
+ permissionReadCl: true,
+ permissionReadTosca: true,
+ permissionUpdateCl: true,
+ permissionUpdateTemplate: true,
+ permissionUpdateTosca: true,
+ userName: 'admin',
+ cldsVersion: '1.0.0'
+ }
+ };
+
+ }
+ initialValues() {
+ UserService.getUserInfo().then(userInfo => {
+ this.setState({ userInfo: userInfo })
+ });
+ }
+ handleClose() {
+ this.props.history.push('/');
+ }
+ renderReadTemplatePermission() {
+ if (this.state.userInfo["permissionReadTemplate"]) {
+ return <Form.Control plaintext readOnly defaultValue="Read Template" />
+ } else {
+ return;
+ }
+ }
+ renderReadModelPermission() {
+ if (this.state.userInfo["permissionReadCl"]) {
+ return <Form.Control plaintext readOnly defaultValue="Read Model" />
+ } else {
+ return;
+ }
+ }
+ renderReadToscaPermission() {
+ if (this.state.userInfo["permissionReadTosca"]) {
+ return <Form.Control plaintext readOnly defaultValue="Read Tosca" />
+ } else {
+ return;
+ }
+ }
+ renderUpdateTemplatePermission() {
+ if (this.state.userInfo["permissionUpdateTemplate"]) {
+ return <Form.Control plaintext readOnly defaultValue="Edit Template" />
+ } else {
+ return;
+ }
+ }
+ renderUpdateModelPermission() {
+ if (this.state.userInfo["permissionUpdateCl"]) {
+ return <Form.Control plaintext readOnly defaultValue="Edit Model" />
+ } else {
+ return;
+ }
+ }
+ renderUpdateToscaPermission() {
+ if (this.state.userInfo["permissionUpdateTosca"]) {
+ return <Form.Control plaintext readOnly defaultValue="Edit Tosca" />
+ } else {
+ return;
+ }
+ }
+ renderUserName() {
+ if (this.state.userInfo["userName"]) {
+ return <Form.Control plaintext readOnly defaultValue={this.state.userInfo["userName"]} />
+ } else {
+ return;
+ }
+ }
+ renderVersion() {
+ if (this.state.userInfo["cldsVersion"]) {
+ return <Form.Control plaintext readOnly defaultValue={this.state.userInfo["cldsVersion"]} />
+ } else {
+ return;
+ }
+ }
+ render() {
+ return (
+ <ModalStyled size="lg" show={this.state.show} onHide={this.handleClose} onEntered={this.initialValues}>
+ <Modal.Header closeButton>
+ <Modal.Title>User Info</Modal.Title>
+ </Modal.Header>
+ <Modal.Body>
+ <Form.Group as={Row} controlId="userName">
+ <Form.Label column sm="3">Current User:</Form.Label>
+ <Col>{this.renderUserName()}</Col>
+ </Form.Group>
+ <Form.Group as={Row} controlId="cldsVersion">
+ <Form.Label column sm="3">CLDS Version:</Form.Label>
+ <Col>{this.renderVersion()}</Col>
+ </Form.Group>
+ <Form.Group as={Row} controlId="userPermissions">
+ <Form.Label column sm="3">User Permissions:</Form.Label>
+ <Col>
+ {this.renderReadTemplatePermission()}
+ {this.renderReadModelPermission()}
+ {this.renderReadToscaPermission()}
+ {this.renderUpdateTemplatePermission()}
+ {this.renderUpdateModelPermission()}
+ {this.renderUpdateToscaPermission()}
+ </Col>
+ </Form.Group>
+ </Modal.Body>
+ <Modal.Footer>
+ <Button variant="secondary" type="null" onClick={this.handleClose}>Cacel</Button>
+ </Modal.Footer>
+ </ModalStyled>
+ );
+ }
+}
diff --git a/ui-react/src/components/loop_viewer/logs/LoopLogs.js b/ui-react/src/components/loop_viewer/logs/LoopLogs.js
new file mode 100644
index 000000000..b6a777a40
--- /dev/null
+++ b/ui-react/src/components/loop_viewer/logs/LoopLogs.js
@@ -0,0 +1,97 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2019 AT&T Intellectual Property. All rights
+ * reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+import React from 'react';
+import Table from 'react-bootstrap/Table';
+import LoopCache from '../../../api/LoopCache';
+import styled from 'styled-components';
+
+const LoopLogsHeaderDivStyled = styled.div`
+ background-color: ${props => props.theme.loopViewerHeaderBackgroundColor};
+ padding: 10px 10px;
+ color: ${props => props.theme.loopViewerHeaderFontColor};
+`
+const TableStyled = styled(Table)`
+
+ overflow: auto;
+`
+const TableRow = ({ logRow }) => (
+ <tr>
+ <td>{logRow.logInstant}</td>
+ <td>{logRow.logType}</td>
+ <td>{logRow.logComponent}</td>
+ <td>{logRow.message}</td>
+ </tr>
+
+)
+
+export default class LoopLogs extends React.Component {
+
+ state = {
+ loopCache: new LoopCache({}),
+ }
+ constructor(props) {
+ super(props);
+ this.renderLogs = this.renderLogs.bind(this);
+ this.state.loopCache = props.loopCache;
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ return this.state.loopCache !== nextState.loopCache;
+ }
+
+ componentWillReceiveProps(newProps) {
+ this.setState({
+ loopCache: newProps.loopCache,
+ });
+ }
+
+ renderLogs() {
+ if (this.state.loopCache.getLoopLogsArray() != null) {
+ return (
+ this.state.loopCache.getLoopLogsArray().map(row => <TableRow logRow={row} />)
+ )
+ }
+ }
+
+ render() {
+ return (
+ <LoopLogsHeaderDivStyled>
+ <label>Loop Logs</label>
+ <TableStyled striped hover variant responsive>
+ <thead>
+ <tr>
+ <th><span align="left">Date</span></th>
+ <th><span align="left">Type</span></th>
+ <th><span align="left">Component</span></th>
+ <th><span align="right">Log</span></th>
+ </tr>
+ </thead>
+ <tbody>
+ {this.renderLogs()}
+ </tbody>
+ </TableStyled>
+ </LoopLogsHeaderDivStyled>
+
+ );
+ }
+}
diff --git a/ui-react/src/components/loop_viewer/status/LoopStatus.js b/ui-react/src/components/loop_viewer/status/LoopStatus.js
index f904d6740..141a41f51 100644
--- a/ui-react/src/components/loop_viewer/status/LoopStatus.js
+++ b/ui-react/src/components/loop_viewer/status/LoopStatus.js
@@ -22,34 +22,84 @@
*/
import React from 'react';
import Table from 'react-bootstrap/Table';
-import './LoopStatus.css';
+import styled from 'styled-components';
+import LoopCache from '../../../api/LoopCache';
+
+const LoopStatusViewDivStyled = styled.div`
+ background-color: ${props => props.theme.loopViewerHeaderBackgroundColor};
+ padding: 10px 10px;
+ color: ${props => props.theme.loopViewerHeaderFontColor};
+`
+
+const TableStyled = styled(Table)`
+ overflow: auto;
+`
+
+const TableRow = ({ statusRow }) => (
+ <tr>
+ <td>{statusRow.componentName}</td>
+ <td>{statusRow.stateName}</td>
+ <td>{statusRow.description}</td>
+ </tr>
+
+)
export default class LoopStatus extends React.Component {
- render() {
- return (
- <div>
- <span id="status_clds" className="status_title">Status:
- <span className="status">&nbsp;&nbsp;&nbsp;TestStatus&nbsp;&nbsp;&nbsp;</span>
- </span>
-
- <div className="status_table">
- <Table striped hover>
- <thead>
- <tr>
- <th><span align="left" className="text">ComponentState</span></th>
- <th><span align="left" className="text">Description</span></th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td className="row_30_per">long test State</td>
- <td className="row_70_per">test description very very very long description</td>
- </tr>
- </tbody>
- </Table>
- </div>
- </div>
- );
- }
+ state = {
+ loopCache: new LoopCache({}),
+ }
+
+ constructor(props) {
+ super(props);
+ this.renderStatus = this.renderStatus.bind(this);
+ this.state.loopCache = props.loopCache;
+ }
+
+
+ renderStatus() {
+ if (this.state.loopCache.getComponentStates() != null) {
+ return Object.keys(this.state.loopCache.getComponentStates()).map((key) => {
+ console.debug("Adding status for: ",key);
+ var res={}
+ res[key]=this.state.loopCache.getComponentStates()[key];
+ return (<TableRow statusRow={{'componentName':key,'stateName':this.state.loopCache.getComponentStates()[key].componentState.stateName,'description':this.state.loopCache.getComponentStates()[key].componentState.description}} />)
+ })
+
+ }
+ }
+
+ shouldComponentUpdate(nextProps, nextState) {
+ return this.state.loopCache !== nextState.loopCache;
+ }
+
+ componentWillReceiveProps(newProps) {
+ this.setState({
+ loopCache: newProps.loopCache,
+ });
+ }
+
+ render() {
+ return (
+ <LoopStatusViewDivStyled>
+ <label>Loop Status: {this.state.loopCache.getComputedState()}
+ </label>
+
+ <div >
+ <TableStyled striped hover variant responsive>
+ <thead>
+ <tr>
+ <th><span align="left">Component Name</span></th>
+ <th><span align="left">Component State</span></th>
+ <th><span align="right">Description</span></th>
+ </tr>
+ </thead>
+ <tbody>
+ {this.renderStatus()}
+ </tbody>
+ </TableStyled>
+ </div>
+ </LoopStatusViewDivStyled>
+ );
+ }
}
diff --git a/ui-react/src/components/loop_viewer/svg/LoopComponentConverter.js b/ui-react/src/components/loop_viewer/svg/LoopComponentConverter.js
new file mode 100644
index 000000000..a409d2cd0
--- /dev/null
+++ b/ui-react/src/components/loop_viewer/svg/LoopComponentConverter.js
@@ -0,0 +1,18 @@
+export default class LoopComponentConverter {
+
+ static buildMapOfComponents(loopCache) {
+ var componentsMap = new Map([]);
+ if (typeof (loopCache.getMicroServicePolicies()) !== "undefined") {
+ loopCache.getMicroServicePolicies().forEach(ms => {
+ componentsMap.set(ms.name, "/configurationPolicyModal/"+ms.name);
+ })
+ }
+ if (typeof (loopCache.getOperationalPolicies()) !== "undefined") {
+ loopCache.getOperationalPolicies().forEach(op => {
+ componentsMap.set(op.name, "/operationalPolicyModal");
+ })
+ }
+ componentsMap.set("OperationalPolicy","/operationalPolicyModal");
+ return componentsMap;
+ }
+}
diff --git a/ui-react/src/components/loop_viewer/svg/LoopSvg.js b/ui-react/src/components/loop_viewer/svg/LoopSvg.js
index 2858ccd8a..3ac2f31fd 100644
--- a/ui-react/src/components/loop_viewer/svg/LoopSvg.js
+++ b/ui-react/src/components/loop_viewer/svg/LoopSvg.js
@@ -22,34 +22,39 @@
*/
import React from 'react';
import styled from 'styled-components';
+import LoopCache from '../../../api/LoopCache';
import { withRouter } from "react-router";
-import LoopCache from '../../../api/LoopCache'
-import LoopService from '../../../api/LoopService'
+import LoopService from '../../../api/LoopService';
+import LoopComponentConverter from './LoopComponentConverter';
const LoopViewSvgDivStyled = styled.div`
overflow: hidden;
background-color: ${props => (props.theme.loopViewerBackgroundColor)};
border: 1px solid;
border-color: ${props => (props.theme.loopViewerHeaderColor)};
- height: 50%;
+ margin-left: auto;
+ margin-right:auto;
+ text-align: center;
+
`
class LoopViewSvg extends React.Component {
static emptySvg = "<svg><text x=\"20\" y=\"40\">No LOOP (SVG)</text></svg>";
- static operationalPolicyDataElementId = "OperationalPolicy";
-
state = {
svgContent: LoopViewSvg.emptySvg,
- loopCache: this.props.loopCache,
+ loopCache: new LoopCache({}),
+ componentModalMapping: new Map([]),
}
constructor(props) {
super(props);
- this.state.loopCache = props.loopCache;
this.handleSvgClick = this.handleSvgClick.bind(this);
this.getSvg = this.getSvg.bind(this);
+ this.state.loopCache = props.loopCache;
+ this.state.componentModalMapping = LoopComponentConverter.buildMapOfComponents(props.loopCache);
+ this.getSvg(props.loopCache.getLoopName());
}
shouldComponentUpdate(nextProps, nextState) {
@@ -57,40 +62,36 @@ class LoopViewSvg extends React.Component {
}
componentWillReceiveProps(newProps) {
- this.state.loopCache = newProps.loopCache;
- this.getSvg();
- }
+ this.setState({
+ loopCache: newProps.loopCache,
+ componentModalMapping: LoopComponentConverter.buildMapOfComponents(newProps.loopCache),
- componentDidMount() {
- this.getSvg();
+ });
+ this.getSvg(newProps.loopCache.getLoopName());
}
- getSvg() {
- LoopService.getSvg(this.state.loopCache.getLoopName()).then(svgXml => {
- if (svgXml.length != 0) {
- this.setState({ svgContent: svgXml })
- } else {
- this.setState({ svgContent: LoopViewSvg.emptySvg })
- }
- });
+ getSvg(loopName) {
+ if (typeof loopName !== "undefined") {
+ LoopService.getSvg(loopName).then(svgXml => {
+ if (svgXml.length !== 0) {
+ this.setState({ svgContent: svgXml })
+ } else {
+ this.setState({ svgContent: LoopViewSvg.emptySvg })
+ }
+ });
+ }
}
handleSvgClick(event) {
console.debug("svg click event received");
var elementName = event.target.parentNode.parentNode.parentNode.getAttribute('data-element-id');
console.info("SVG element clicked", elementName);
- if (typeof elementName === "undefined" || elementName === "VES") {
- return;
- } else if (elementName === LoopViewSvg.operationalPolicyDataElementId) {
- this.props.history.push('/operationalPolicyModal');
- } else {
- this.props.history.push('/configurationPolicyModal');
- }
+ this.props.history.push(this.state.componentModalMapping.get(elementName));
}
render() {
return (
- <LoopViewSvgDivStyled id="loop_svg" dangerouslySetInnerHTML={{ __html: this.state.svgContent }} onClick={this.handleSvgClick}>
+ <LoopViewSvgDivStyled dangerouslySetInnerHTML={{ __html: this.state.svgContent }} onClick={this.handleSvgClick}>
</LoopViewSvgDivStyled>
);
diff --git a/ui-react/src/components/menu/MenuBar.js b/ui-react/src/components/menu/MenuBar.js
index 1a7c5d4e4..491cc675d 100644
--- a/ui-react/src/components/menu/MenuBar.js
+++ b/ui-react/src/components/menu/MenuBar.js
@@ -35,14 +35,13 @@ const StyledNavDropdownItem = styled(NavDropdown.Item)`
`;
export default class MenuBar extends React.Component {
-
render () {
return (
<Navbar.Collapse id="basic-navbar-nav" className="justify-content-center">
<NavDropdown title="Closed Loop" id="basic-nav-dropdown">
<StyledNavDropdownItem href="/openLoop">Open CL</StyledNavDropdownItem>
- <StyledNavDropdownItem href="#action/3.2">Properties CL</StyledNavDropdownItem>
- <StyledNavDropdownItem href="#action/3.3">Close Model</StyledNavDropdownItem>
+ <StyledNavDropdownItem href="/loopProperties">Properties CL</StyledNavDropdownItem>
+ <StyledNavDropdownItem href="/closeLoop">Close Model</StyledNavDropdownItem>
</NavDropdown>
<NavDropdown title="Manage" id="basic-nav-dropdown">
<StyledNavDropdownItem href="/operationalPolicyModal">Submit</StyledNavDropdownItem>
diff --git a/ui-react/src/setupTests.js b/ui-react/src/setupTests.js
new file mode 100644
index 000000000..fc7b0dce1
--- /dev/null
+++ b/ui-react/src/setupTests.js
@@ -0,0 +1,4 @@
+import Enzyme from 'enzyme';
+import Adapter from 'enzyme-adapter-react-16';
+
+Enzyme.configure({ adapter: new Adapter() });
diff --git a/ui-react/src/theme/globalStyle.js b/ui-react/src/theme/globalStyle.js
index 1630c5074..08630cbd9 100644
--- a/ui-react/src/theme/globalStyle.js
+++ b/ui-react/src/theme/globalStyle.js
@@ -56,13 +56,12 @@ export const GlobalClampStyle = createGlobalStyle`
font-size: ${props => props.theme.fontSize};
border-radius: 4px;
color: ${props => props.theme.fontNormal};
- background-color: ${props => (props.theme.backgroundColor)};
+ background-color: ${props => (props.theme.backgroundColor)};
+ margin-top: 1px;
}
-
+
svg {
- padding: 10px;
overflow: hidden;
- background-color: ${props => (props.theme.loopViewerBackgroundColor)};
width: 100%;
height: 100%;
}
@@ -75,13 +74,13 @@ export const DefaultClampTheme = {
fontDark: '#888888',
fontHighlight: '#ffff00',
fontNormal: 'black',
-
+
backgroundColor: '#eeeeee',
fontFamily: 'Arial, Sans-serif',
fontSize: '15px',
-
- loopViewerBackgroundColor: 'white',
- loopViewerFontColor: 'yellow',
- loopViewerHeaderBackgroundColor: '#337ab7',
- loopViewerHeaderFontColor: 'white',
+
+ loopViewerBackgroundColor: 'white',
+ loopViewerFontColor: 'yellow',
+ loopViewerHeaderBackgroundColor: '#337ab7',
+ loopViewerHeaderFontColor: 'white',
};