Add remove op policy option 22/103522/5
authorxuegao <xg353y@intl.att.com>
Wed, 11 Mar 2020 10:22:15 +0000 (11:22 +0100)
committerxuegao <xg353y@intl.att.com>
Fri, 13 Mar 2020 15:16:54 +0000 (16:16 +0100)
Update the UI and backend code to be able to remove op policy for loop
instances.

Issue-ID: CLAMP-648
Change-Id: Ib3eab4977fe4f1b85e11f2373263197009bbc3e1
Signed-off-by: xuegao <xg353y@intl.att.com>
src/main/java/org/onap/clamp/loop/Loop.java
src/main/java/org/onap/clamp/loop/LoopController.java
src/main/java/org/onap/clamp/loop/LoopService.java
src/main/resources/clds/camel/rest/clamp-api-v2.xml
src/test/java/org/onap/clamp/loop/LoopControllerTestItCase.java
ui-react/src/api/LoopService.js
ui-react/src/components/dialogs/Loop/ModifyLoopModal.js
ui-react/src/components/dialogs/Loop/ModifyLoopModal.test.js [new file with mode: 0644]

index f185460..2bf3dec 100644 (file)
@@ -270,6 +270,17 @@ public class Loop extends AuditEntity implements Serializable {
         this.setSvgRepresentation(SvgLoopGenerator.getSvgImage(this));
     }
 
+    /**
+     * This method removes an operational policy to the loop.
+     * It re-computes the Svg as well.
+     *
+     * @param opPolicy the operationalPolicy to add
+     */
+    public void removeOperationalPolicy(OperationalPolicy opPolicy) {
+        operationalPolicies.remove(opPolicy);
+        this.setSvgRepresentation(SvgLoopGenerator.getSvgImage(this));
+    }
+
     /**
      * This method adds an micro service policy to the loop.
      * It re-computes the Svg as well.
index 1a67455..1a4ae59 100644 (file)
@@ -109,6 +109,18 @@ public class LoopController {
         return loopService.addOperationalPolicy(loopName, policyType, policyVersion);
     }
 
+    /**
+     * This method remove an operational policy to a loop instance.
+     *
+     * @param loopName      The loop name
+     * @param policyType    The policy model type
+     * @param policyVersion The policy model  version
+     * @return The loop modified
+     */
+    public Loop removeOperationalPolicy(String loopName, String policyType, String policyVersion) {
+        return loopService.removeOperationalPolicy(loopName, policyType, policyVersion);
+    }
+
     /**
      * This method deletes the loop.
      *
index 98a2fbd..953a594 100644 (file)
@@ -120,7 +120,31 @@ public class LoopService {
                 new OperationalPolicy(Policy.generatePolicyName("OPERATIONAL", loop.getModelService().getName(),
                         loop.getModelService().getVersion(), RandomStringUtils.randomAlphanumeric(3),
                         RandomStringUtils.randomAlphanumeric(4)), loop, null, policyModel, null, null, null));
-        return loopsRepository.save(loop);
+        return loopsRepository.saveAndFlush(loop);
+    }
+
+    /**
+     * This method remove an operational policy to a loop instance.
+     *
+     * @param loopName The loop name
+     * @param policyType The policy model type
+     * @param policyVersion The policy model  version
+     * @return The loop modified
+     */
+    Loop removeOperationalPolicy(String loopName, String policyType, String policyVersion) {
+        Loop loop = getLoop(loopName);
+        PolicyModel policyModel = policyModelsService.getPolicyModel(policyType, policyVersion);
+        if (policyModel == null) {
+            return null;
+        }
+        for (OperationalPolicy opPolicy : loop.getOperationalPolicies()) {
+            if (opPolicy.getPolicyModel().getPolicyModelType().equals(policyType) &&
+                    opPolicy.getPolicyModel().getVersion().equals(policyVersion)) {
+                loop.removeOperationalPolicy(opPolicy);
+                break;
+            }
+        }
+        return loopsRepository.saveAndFlush(loop);
     }
 
     Loop updateAndSaveOperationalPolicies(String loopName, List<OperationalPolicy> newOperationalPolicies) {
index fbf9071..d1c191d 100644 (file)
                                </doTry>
                        </route>
                </put>
+               <put uri="/v2/loop/removeOperationaPolicy/{loopName}/policyModel/{policyType}/{policyVersion}" outType="org.onap.clamp.loop.Loop" produces="application/json">
+                       <route>
+                               <removeHeaders pattern="*" excludePattern="loopName|policyType|policyVersion" />
+                               <doTry>
+                                       <to
+                                                       uri="bean:org.onap.clamp.flow.log.FlowLogOperation?method=startLog(*, 'REMOVE operational Policy')" />
+                                       <to
+                                                       uri="bean:org.onap.clamp.authorization.AuthorizationController?method=authorize(*,'cl','','update')" />
+                                       <to uri="direct:load-loop" />
+                                       <to
+                                                       uri="bean:org.onap.clamp.loop.LoopController?method=removeOperationalPolicy(${header.loopName},${header.policyType},${header.policyVersion})" />
+                                       <to
+                                                       uri="bean:org.onap.clamp.loop.log.LoopLogService?method=addLog('REMOVE OperationalPolicy request successfully executed','INFO',${exchangeProperty[loopObject]})" />
+                                       <to
+                                                       uri="bean:org.onap.clamp.flow.log.FlowLogOperation?method=endLog()" />
+                                       <doCatch>
+                                               <exception>java.lang.Exception</exception>
+                                               <handled>
+                                                       <constant>false</constant>
+                                               </handled>
+                                               <to
+                                                               uri="bean:org.onap.clamp.flow.log.FlowLogOperation?method=errorLog()" />
+                                               <log loggingLevel="ERROR"
+                                                        message="REMOVE OperationalPolicy request failed for loop: ${header.loopName}" />
+                                               <to
+                                                               uri="bean:org.onap.clamp.loop.log.LoopLogService?method=addLog('REMOVE OperationalPolicy request failed','ERROR',${exchangeProperty[loopObject]})" />
+                                       </doCatch>
+                               </doTry>
+                       </route>
+               </put>
                <post
                                uri="/v2/loop/create/{loopName}?templateName={templateName}"
                                outType="org.onap.clamp.loop.Loop" consumes="application/json"
index f017dd8..24a9037 100644 (file)
@@ -36,14 +36,17 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.onap.clamp.clds.Application;
 import org.onap.clamp.clds.util.JsonUtils;
+import org.onap.clamp.loop.service.Service;
 import org.onap.clamp.loop.template.LoopTemplate;
 import org.onap.clamp.loop.template.PolicyModel;
 import org.onap.clamp.loop.template.PolicyModelsService;
 import org.onap.clamp.policy.microservice.MicroServicePolicy;
 import org.onap.clamp.policy.microservice.MicroServicePolicyService;
 import org.onap.clamp.policy.operational.OperationalPolicy;
+import org.onap.clamp.policy.operational.OperationalPolicyService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.boot.test.context.SpringBootTest;
+import org.springframework.test.annotation.Commit;
 import org.springframework.test.context.junit4.SpringRunner;
 
 @RunWith(SpringRunner.class)
@@ -62,6 +65,9 @@ public class LoopControllerTestItCase {
     @Autowired
     MicroServicePolicyService microServicePolicyService;
 
+    @Autowired
+    OperationalPolicyService operationalPolicyService;
+
     @Autowired
     PolicyModelsService policyModelsService;
 
@@ -74,6 +80,8 @@ public class LoopControllerTestItCase {
         LoopTemplate template =  new LoopTemplate();
         template.setName("testTemplate");
         testLoop.setLoopTemplate(template);
+        Service modelService = new Service("{\"name\":\"serviceName\",\"UUID\":\"uuid\"}","{}");
+        testLoop.setModelService(modelService);
         loopService.saveOrUpdateLoop(testLoop);
     }
 
@@ -131,7 +139,7 @@ public class LoopControllerTestItCase {
     @Transactional
     public void testUpdateMicroservicePolicy() {
         saveTestLoopToDb();
-        PolicyModel policyModel = new PolicyModel("",
+        PolicyModel policyModel = new PolicyModel("testPolicyModel",
                 "tosca_definitions_version: tosca_simple_yaml_1_0_0","1.0.0");
         policyModelsService.saveOrUpdatePolicyModel(policyModel);
         MicroServicePolicy policy = new MicroServicePolicy("policyName", policyModel, false,
@@ -147,4 +155,28 @@ public class LoopControllerTestItCase {
         String svgRepresentation = loopController.getSvgRepresentation(EXAMPLE_LOOP_NAME);
         assertThat(svgRepresentation).isEqualTo("representation");
     }
+
+    @Test
+    @Transactional
+    public void testAddAndRemoveOperationalPolicies() {
+        saveTestLoopToDb();
+        PolicyModel policyModel = new PolicyModel("testPolicyModel",
+                "tosca_definitions_version: tosca_simple_yaml_1_0_0","1.0.0");
+        policyModelsService.saveOrUpdatePolicyModel(policyModel);
+
+        loopController.addOperationalPolicy(EXAMPLE_LOOP_NAME, "testPolicyModel", "1.0.0");
+
+        Loop newLoop = loopController.getLoop(EXAMPLE_LOOP_NAME);
+        Set<OperationalPolicy> opPolicyList = newLoop.getOperationalPolicies();
+        assertThat(opPolicyList.size()).isEqualTo(1);
+        for(OperationalPolicy policy : opPolicyList) {
+            assertThat(policy.getName().contains("OPERATIONAL_serviceName")).isTrue();
+            assertThat(policy.getPolicyModel().getPolicyModelType()).isEqualTo("testPolicyModel");
+            assertThat(policy.getPolicyModel().getVersion()).isEqualTo("1.0.0");
+        }
+
+        loopController.removeOperationalPolicy(EXAMPLE_LOOP_NAME, "testPolicyModel", "1.0.0");
+        Loop newLoop2 = loopController.getLoop(EXAMPLE_LOOP_NAME);
+        assertThat(newLoop2.getOperationalPolicies().size()).isEqualTo(0);
+    }
 }
\ No newline at end of file
index d665f81..2750763 100644 (file)
@@ -194,26 +194,49 @@ export default class LoopService {
                        });
        }
 
-               static addOperationalPolicyType(loopName, policyType, policyVersion) {
-               return fetch('/restservices/clds/v2/loop/addOperationaPolicy/' + loopName + '/policyModel/' + policyType +'/' + policyVersion , {
-                       method: 'PUT',
-                       headers: {
-                               "Content-Type": "application/json"
-                       },
-                       credentials: 'same-origin'
-               })
-                .then(function (response) {
-                    console.debug("Add Operational Policy response received: ", response.status);
-                               if (response.ok) {
-                                       return response.json();
-                               } else {
-                                       console.error("Add Operational Policy query failed");
-                                       return {};
-                               }
-                       })
-                       .catch(function (error) {
-                               console.error("Add Operational Policy error received", error);
-                               return {};
-                       });
-       }
+       static addOperationalPolicyType(loopName, policyType, policyVersion) {
+               return fetch('/restservices/clds/v2/loop/addOperationaPolicy/' + loopName + '/policyModel/' + policyType +'/' + policyVersion , {
+                       method: 'PUT',
+                       headers: {
+                               "Content-Type": "application/json"
+                       },
+                       credentials: 'same-origin'
+               })
+                               .then(function (response) {
+                                       console.debug("Add Operational Policy response received: ", response.status);
+                               if (response.ok) {
+                                       return response.json();
+                               } else {
+                                       console.error("Add Operational Policy query failed");
+                                       return {};
+                               }
+                       })
+                       .catch(function (error) {
+                               console.error("Add Operational Policy error received", error);
+                               return {};
+                       });
+       }
+
+       static removeOperationalPolicyType(loopName, policyType, policyVersion) {
+               return fetch('/restservices/clds/v2/loop/removeOperationaPolicy/' + loopName + '/policyModel/' + policyType +'/' + policyVersion , {
+                       method: 'PUT',
+                       headers: {
+                               "Content-Type": "application/json"
+                       },
+                       credentials: 'same-origin'
+               })
+                               .then(function (response) {
+                                       console.debug("Remove Operational Policy response received: ", response.status);
+                               if (response.ok) {
+                                       return response.json();
+                               } else {
+                                       console.error("Remove Operational Policy query failed");
+                                       return {};
+                               }
+                       })
+                       .catch(function (error) {
+                               console.error("Remove Operational Policy error received", error);
+                               return {};
+                       });
+       }
 }
index 4cd21fd..7c16b78 100644 (file)
@@ -35,6 +35,8 @@ import FirstPage from '@material-ui/icons/FirstPage';
 import LastPage from '@material-ui/icons/LastPage';
 import Search from '@material-ui/icons/Search';
 import LoopService from '../../../api/LoopService';
+import Tabs from 'react-bootstrap/Tabs';
+import Tab from 'react-bootstrap/Tab';
 
 
 const ModalStyled = styled(Modal)`
@@ -61,6 +63,8 @@ export default class ModifyLoopModal extends React.Component {
                content: 'Please select Tosca model to view the details',
                selectedRowData: {},
                toscaPolicyModelsData: [],
+               selectedPolicyModelsData: [],
+               key: 'add',
                toscaColumns: [
                        { title: "#", field: "index", render: rowData => rowData.tableData.id + 1,
                                cellStyle: cellStyle,
@@ -105,11 +109,12 @@ export default class ModifyLoopModal extends React.Component {
        constructor(props, context) {
                super(props, context);
                this.handleClose = this.handleClose.bind(this);
-               this.getPolicyToscaModels = this.getToscaPolicyModels.bind(this);
+               this.initializeToscaPolicyModelsInfo = this.initializeToscaPolicyModelsInfo.bind(this);
                this.handleYamlContent = this.handleYamlContent.bind(this);
                this.getToscaPolicyModelYaml = this.getToscaPolicyModelYaml.bind(this);
                this.handleAdd = this.handleAdd.bind(this);
-               this.getToscaPolicyModels();
+               this.handleRemove = this.handleRemove.bind(this);
+               this.initializeToscaPolicyModelsInfo();
        }
 
        componentWillReceiveProps(newProps) {
@@ -119,9 +124,16 @@ export default class ModifyLoopModal extends React.Component {
                });
        }
 
-       getToscaPolicyModels() {
-           PolicyToscaService.getToscaPolicyModels().then(allToscaModels => {
-                       this.setState({ toscaPolicyModelsData: allToscaModels});
+       initializeToscaPolicyModelsInfo() {
+               var operationalPolicies = this.state.loopCache.getOperationalPolicies();
+               var selectedPolicyModels = [];
+               for (var policy in operationalPolicies) {
+                       selectedPolicyModels.push(operationalPolicies[policy]["policyModel"]);
+               }
+
+               PolicyToscaService.getToscaPolicyModels().then(allToscaModels => {
+                       this.setState({ toscaPolicyModelsData: allToscaModels,
+                                                       selectedPolicyModelsData: selectedPolicyModels});
                });
        }
 
@@ -149,38 +161,70 @@ export default class ModifyLoopModal extends React.Component {
        }
 
        handleAdd() {
-        LoopService.addOperationalPolicyType(this.state.loopCache.getLoopName(),this.state.selectedRowData.policyModelType,this.state.selectedRowData.version);
-        this.handleClose();
-        this.props.loadLoopFunction(this.state.loopCache.getLoopName());
+               LoopService.addOperationalPolicyType(this.state.loopCache.getLoopName(),this.state.selectedRowData.policyModelType,this.state.selectedRowData.version);
+               this.handleClose();
+               this.props.loadLoopFunction(this.state.loopCache.getLoopName());
+       }
+
+       handleRemove() {
+               LoopService.removeOperationalPolicyType(this.state.loopCache.getLoopName(),this.state.selectedRowData.policyModelType,this.state.selectedRowData.version);
+               this.handleClose();
+               this.props.loadLoopFunction(this.state.loopCache.getLoopName());
        }
 
        render() {
                return (
                        <ModalStyled size="xl" show={this.state.show} onHide={this.handleClose}>
                                <Modal.Header closeButton>
+                                       <Modal.Title>Modify Loop Operational Policies</Modal.Title>
                                </Modal.Header>
-                               <Modal.Body>
-                                       <MaterialTable
-                                       title={"View Tosca Policy Models"}
-                                       data={this.state.toscaPolicyModelsData}
-                                       columns={this.state.toscaColumns}
-                                       icons={this.state.tableIcons}
-                                       onRowClick={(event, rowData) => {this.getToscaPolicyModelYaml(rowData.policyModelType, rowData.version);this.setState({selectedRowData: rowData})}}
-                                       options={{
-                                               headerStyle: rowHeaderStyle,
-                                               rowStyle: rowData => ({
-                                                       backgroundColor: (this.state.selectedRowData !== {} && this.state.selectedRowData.id === rowData.tableData.id) ? '#EEE' : '#FFF'
-                                               })
-                                       }}
-                                       />
-                                       <div>
-                                               <TextModal value={this.state.content} onChange={this.handleYamlContent}/>
-                                       </div>
-                               </Modal.Body>
+                               <Tabs id="controlled-tab-example" activeKey={this.state.key} onSelect={key => this.setState({ key, selectedRowData: {} })}>
+                                       <Tab eventKey="add" title="Add Operational Policies">
+                                               <Modal.Body>
+                                                       <MaterialTable
+                                                       title={"View Tosca Policy Models"}
+                                                       data={this.state.toscaPolicyModelsData}
+                                                       columns={this.state.toscaColumns}
+                                                       icons={this.state.tableIcons}
+                                                       onRowClick={(event, rowData) => {this.getToscaPolicyModelYaml(rowData.policyModelType, rowData.version);this.setState({selectedRowData: rowData})}}
+                                                       options={{
+                                                               headerStyle: rowHeaderStyle,
+                                                               rowStyle: rowData => ({
+                                                                       backgroundColor: (this.state.selectedRowData !== {} && this.state.selectedRowData.tableData !== undefined
+                                                                       && this.state.selectedRowData.tableData.id === rowData.tableData.id) ? '#EEE' : '#FFF'
+                                                               })
+                                                       }}
+                                                       />
+                                                       <div>
+                                                               <TextModal value={this.state.content} onChange={this.handleYamlContent}/>
+                                                       </div>
+                                               </Modal.Body>
+                                       </Tab>
+                                       <Tab eventKey="remove" title="Remove Operational Policies">
+                                               <Modal.Body>
+                                                       <MaterialTable
+                                                       title={"Already added Tosca Policy Models"}
+                                                       data={this.state.selectedPolicyModelsData}
+                                                       columns={this.state.toscaColumns}
+                                                       icons={this.state.tableIcons}
+                                                       onRowClick={(event, rowData) => {this.setState({selectedRowData: rowData})}}
+                                                       options={{
+                                                               headerStyle: rowHeaderStyle,
+                                                               rowStyle: rowData => ({
+                                                                       backgroundColor: (this.state.selectedRowData !== {} && this.state.selectedRowData.tableData !== undefined
+                                                                       && this.state.selectedRowData.tableData.id === rowData.tableData.id) ? '#EEE' : '#FFF'
+                                                               })
+                                                       }}
+                                                       />
+                                               </Modal.Body>
+                                       </Tab>
+                               </Tabs>
                                <Modal.Footer>
                                        <Button variant="secondary" type="null" onClick={this.handleClose}>Cancel</Button>
-                                       <Button variant="primary"  type="submit" onClick={this.handleAdd}>Add</Button>
+                                       <Button variant="primary" disabled={(this.state.key === "remove")} type="submit" onClick={this.handleAdd}>Add</Button>
+                                       <Button variant="primary" disabled={(this.state.key === "add")} type="submit" onClick={this.handleRemove}>Remove</Button>
                                </Modal.Footer>
+
                        </ModalStyled>
                );
        }
diff --git a/ui-react/src/components/dialogs/Loop/ModifyLoopModal.test.js b/ui-react/src/components/dialogs/Loop/ModifyLoopModal.test.js
new file mode 100644 (file)
index 0000000..055ad0e
--- /dev/null
@@ -0,0 +1,109 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP CLAMP
+ * ================================================================================
+ * Copyright (C) 2020 AT&T Intellectual Property. All rights
+ *                             reserved.
+ * ================================================================================
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ * ============LICENSE_END============================================
+ * ===================================================================
+ *
+ */
+import React from 'react';
+import { mount } from 'enzyme';
+import ModifyLoopModal from './ModifyLoopModal';
+import LoopCache from '../../../api/LoopCache';
+import LoopService from '../../../api/LoopService';
+import PolicyToscaService from '../../../api/PolicyToscaService';
+
+describe('Verify ModifyLoopModal', () => {
+    beforeEach(() => {
+        PolicyToscaService.getToscaPolicyModels = jest.fn().mockImplementation(() => {
+            return Promise.resolve([{
+                "policyModelType":"test",
+                "policyAcronym":"test",
+                "version":"1.0.0",
+                "updatedBy":"",
+                "updatedDate":""
+            }]);
+        });
+        PolicyToscaService.getToscaPolicyModelYaml = jest.fn().mockImplementation(() => {
+            return Promise.resolve("OK");
+        });
+    })
+
+    const loopCache = new LoopCache({
+            "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+            "microServicePolicies": [{
+                "name": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca",
+                "modelType": "onap.policies.monitoring.cdap.tca.hi.lo.app",
+                "properties": {"domain": "measurementsForVfScaling"},
+                "shared": false,
+                "jsonRepresentation": {"schema": {}}
+            }],
+            "globalPropertiesJson": {
+                "dcaeDeployParameters": {
+                    "testMs": {
+                    "location_id": "",
+                    "policy_id": "TCA_h2NMX_v1_0_ResourceInstanceName1_tca"
+                }
+            }
+        }
+    });
+    const historyMock = { push: jest.fn() };
+    const flushPromises = () => new Promise(setImmediate);
+
+    it('Test handleClose', () => {
+        const handleClose = jest.spyOn(ModifyLoopModal.prototype,'handleClose');
+        const component = mount(<ModifyLoopModal history={historyMock} loopCache={loopCache}/>)
+
+        component.find('[variant="secondary"]').get(0).props.onClick();
+
+        expect(handleClose).toHaveBeenCalledTimes(1);
+        expect(component.state('show')).toEqual(false);
+        expect(historyMock.push.mock.calls[0]).toEqual([ '/']);
+    });
+
+    it('Test getToscaPolicyModelYaml',  async () => {
+        const flushPromises = () => new Promise(setImmediate);
+        const component = mount(<ModifyLoopModal history={historyMock} loopCache={loopCache}/>)
+        component.setState({ 
+                "selectedRowData": {"tableData":{"id":0}}
+        });
+        const instance = component.instance();
+
+        instance.getToscaPolicyModelYaml("","1.0.0");
+        expect(component.state('content')).toEqual("Please select Tosca model to view the details");
+
+        instance.getToscaPolicyModelYaml("test","1.0.0");
+        await flushPromises();
+        expect(component.state('content')).toEqual("OK");
+
+        PolicyToscaService.getToscaPolicyModelYaml = jest.fn().mockImplementation(() => {
+            return Promise.resolve("");
+        });
+        instance.getToscaPolicyModelYaml("test","1.0.0");
+        await flushPromises();
+        expect(component.state('content')).toEqual("No Tosca model Yaml available");
+    });
+
+    it('Test handleYamlContent',  async () => {
+        const component = mount(<ModifyLoopModal loopCache={loopCache}/>)
+        const instance = component.instance();
+
+        const event = {"target":{"value":"testValue"}}
+        instance.handleYamlContent(event);
+        expect(component.state('content')).toEqual("testValue");
+    });
+});
\ No newline at end of file