ToscaFileSelector 78/120778/9
authorsebdet <sebastien.determe@intl.att.com>
Wed, 21 Apr 2021 14:28:33 +0000 (16:28 +0200)
committersebdet <sebastien.determe@intl.att.com>
Fri, 23 Apr 2021 15:35:17 +0000 (17:35 +0200)
The UI code that manages the tosca file selector that will be sent to clamp be + fix Rest Camel endpoint that always want ot convert http body to JSON (due to Camel upgrade)

Issue-ID: POLICY-3229
Signed-off-by: sebdet <sebastien.determe@intl.att.com>
Change-Id: I6148276d4c527f29db904a76655055c19d4e2023

src/main/java/org/onap/policy/clamp/clds/config/sdc/SdcControllersConfiguration.java
src/main/java/org/onap/policy/clamp/configuration/ClampGsonDataFormat.java
src/main/resources/clds/camel/rest/clamp-api-v2.xml
src/main/resources/clds/camel/routes/policy-flows.xml
ui-react-lib/libIndex.js
ui-react/src/api/PolicyService.js
ui-react/src/components/dialogs/Policy/PolicyToscaFileSelector.js [new file with mode: 0644]
ui-react/src/components/dialogs/Policy/ViewAllPolicies.js

index d0b116f..5d8cbb0 100644 (file)
@@ -27,7 +27,6 @@ package org.onap.policy.clamp.clds.config.sdc;
 
 import com.google.gson.JsonObject;
 import java.io.IOException;
-import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.nio.charset.StandardCharsets;
 import java.util.HashMap;
@@ -38,7 +37,6 @@ import org.onap.policy.clamp.clds.util.JsonUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.ApplicationContext;
-import org.springframework.core.io.Resource;
 
 /**
  * This class maps the SDC config JSON file. This JSON can have multiple
index 95d1fe1..6479cf7 100644 (file)
@@ -36,6 +36,7 @@ import org.apache.camel.spi.DataFormat;
 import org.apache.camel.spi.DataFormatName;
 import org.apache.camel.support.service.ServiceSupport;
 import org.apache.camel.util.IOHelper;
+import org.apache.commons.io.IOUtils;
 import org.onap.policy.clamp.clds.util.JsonUtils;
 
 public class ClampGsonDataFormat extends ServiceSupport implements DataFormat, DataFormatName {
@@ -113,7 +114,9 @@ public class ClampGsonDataFormat extends ServiceSupport implements DataFormat, D
     public Object unmarshal(final Exchange exchange, final InputStream stream) throws Exception {
         try (final InputStreamReader isr = new InputStreamReader(stream, StandardCharsets.UTF_8);
                 final BufferedReader reader = IOHelper.buffered(isr)) {
-            if (unmarshalGenericType == null) {
+            if (unmarshalType.equals(String.class)) {
+                return IOUtils.toString(reader);
+            } else if (unmarshalGenericType == null) {
                 return gson.fromJson(reader, unmarshalType);
             } else {
                 return gson.fromJson(reader, unmarshalGenericType);
index 3238822..c789735 100644 (file)
             </route>
         </put>
 
-        <post uri="/v2/policies/policytype" consumes="application/yaml">
+        <post uri="/v2/policies/policytype" type="java.lang.String" consumes="plain/text">
             <route>
                 <removeHeaders pattern="*"/>
                 <doTry>
                         <simple resultType="java.lang.Boolean">true</simple>
                     </setProperty>
                     <to uri="direct:create-policy-type"/>
+                    <to
+                            uri="bean:org.onap.policy.clamp.policy.downloader.PolicyEngineController?method=synchronizeAllPolicies()"/>
                     <to
                             uri="bean:org.onap.policy.clamp.flow.log.FlowLogOperation?method=endLog()"/>
                     <doCatch>
index 19092bf..0186269 100644 (file)
              <setHeader name="CamelHttpMethod">
                 <constant>POST</constant>
             </setHeader>
+            <setHeader name="Content-Type">
+                <constant>application/yaml</constant>
+            </setHeader>
             <setHeader name="X-ONAP-RequestID">
                 <simple>${exchangeProperty[X-ONAP-RequestID]}
                 </simple>
index 538d648..75d1bc9 100755 (executable)
@@ -53,3 +53,4 @@ export { default as ViewLoopTemplatesModal } from './src/components/dialogs/Tosc
 export { default as ViewAllPolicies } from './src/components/dialogs/Policy/ViewAllPolicies';
 export { default as PolicyDeploymentEditor } from './src/components/dialogs/Policy/PolicyDeploymentEditor';
 export { default as PoliciesTreeViewer } from './src/components/dialogs/Policy/PoliciesTreeViewer';
+export { default as PolicyToscaFileSelector } from './src/components/dialogs/Policy/PolicyToscaFileSelector';
\ No newline at end of file
index 54110f3..f2886b3 100644 (file)
@@ -119,4 +119,30 @@ export default class PolicyService {
             return undefined;
         });
   }
+  static sendNewPolicyModel(newPolicyModel) {
+      return fetch(window.location.pathname + 'restservices/clds/v2/policies/policytype', {
+              method: 'POST',
+              credentials: 'same-origin',
+              headers: {
+                "Content-Type": "plain/text"
+              },
+              body: newPolicyModel
+          })
+          .then(function (response) {
+              console.debug("sendNewPolicyModel response received: ", response.status);
+              if (response.ok) {
+                  console.info("sendNewPolicyModel query successful");
+                  return response.text();
+              } else {
+                 return response.text().then(responseBody => {
+                      throw new Error("HTTP " + response.status + "," + responseBody);
+                  })
+              }
+          })
+          .catch(function (error) {
+              console.error("sendNewPolicyModel error occurred ", error);
+              alert ("sendNewPolicyModel error occurred " + error);
+              return undefined;
+          });
+    }
 }
diff --git a/ui-react/src/components/dialogs/Policy/PolicyToscaFileSelector.js b/ui-react/src/components/dialogs/Policy/PolicyToscaFileSelector.js
new file mode 100644 (file)
index 0000000..8753c78
--- /dev/null
@@ -0,0 +1,127 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP POLICY-CLAMP
+ * ================================================================================
+ * Copyright (C) 2021 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 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 Alert from 'react-bootstrap/Alert';
+import { Input, InputLabel, Button , SvgIcon} from "@material-ui/core";
+import PublishIcon from '@material-ui/icons/Publish';
+import PolicyService from '../../../api/PolicyService';
+
+const ModalStyled = styled(Modal)`
+    background-color: transparent;
+`
+
+const StyledMessagesDiv = styled.div`
+    overflow: auto;
+    max-height: 300px;
+`
+
+
+export default class PolicyToscaFileSelector extends React.Component {
+
+    state = {
+        show: this.props.show,
+        alertMessages: [],
+    }
+    constructor(props, context) {
+        super(props, context);
+        this.handleClose = this.handleClose.bind(this);
+        this.onFileChange = this.onFileChange.bind(this);
+    }
+
+    componentDidUpdate(prevProps) {
+        if (this.props.show !== this.state.show) {
+            this.setState({show: this.props.show});
+        }
+    }
+
+    handleClose() {
+        this.props.disableFunction();
+        this.setState({alertMessages:[]});
+    }
+
+    onFileChange(target) {
+        this.setState({alertMessages:[]});
+        target.currentTarget.files.forEach(file => {
+            const fileReader = new FileReader();
+            fileReader.readAsDataURL(file);
+            fileReader.onload = (content) => {
+                PolicyService.sendNewPolicyModel(atob(content.target.result.split(",")[1])).then(respModelCreate => {
+                      if (typeof(respModelCreate) === "undefined") {
+                        //it indicates a failure
+                        this.setState(state => {
+                            return {
+                                alertMessages: [...state.alertMessages,(<Alert variant="danger"><Alert.Heading>{file.name}</Alert.Heading><p>Policy Tosca Model Creation Failure</p><hr/><p>Type: {file.type}</p><p>Size: {file.size}</p></Alert>)]
+                            };
+                        });
+                      } else {
+                        this.setState(state => {
+                            return {
+                                alertMessages: [...state.alertMessages,(<Alert variant="success"><Alert.Heading>{file.name}</Alert.Heading><p>Policy Tosca Model Created Successfully</p><hr/><p>Type: {file.type}</p><p>Size: {file.size}</p></Alert>)]
+                            };
+                        });
+                      }
+                });
+            };
+        });
+        this.props.toscaTableUpdateFunction();
+    }
+
+    render() {
+        return (
+            <ModalStyled size="lg" show={this.state.show} onHide={this.handleClose} backdrop="static" keyboard={false} >
+                <Modal.Header closeButton>
+                    <Modal.Title>Create New Policy Tosca Model</Modal.Title>
+                </Modal.Header>
+                <Modal.Body>
+                    <Form.Group as={Row} controlId="formPlaintextEmail">
+                        <Col sm="10">
+                           <input type="file" multiple accept=".yaml,.yml" id="fileUploadButton" style={{ display: 'none' }} onChange={this.onFileChange} />
+                           <label htmlFor={'fileUploadButton'}>
+                             <Button color="primary"  variant="contained" component="span"
+                               startIcon={
+                                 <SvgIcon fontSize="small">
+                                   <PublishIcon />
+                                 </SvgIcon>
+                               }>
+                               Upload Files
+                             </Button>
+                             <p>(Only YAML files are supported)</p>
+                           </label>
+                           <StyledMessagesDiv>
+                                {this.state.alertMessages}
+                           </StyledMessagesDiv>
+                        </Col>
+                    </Form.Group>
+                </Modal.Body>
+                <Modal.Footer>
+                    <Button variant="secondary" onClick={this.handleClose}>Close</Button>
+                </Modal.Footer>
+            </ModalStyled>
+        );
+    }
+}
\ No newline at end of file
index 0fd0d13..0496535 100644 (file)
@@ -56,6 +56,7 @@ import PolicyEditor from './PolicyEditor';
 import ToscaViewer from './ToscaViewer';
 import PolicyDeploymentEditor from './PolicyDeploymentEditor';
 import PoliciesTreeViewer from './PoliciesTreeViewer';
+import PolicyToscaFileSelector from './PolicyToscaFileSelector';
 
 const DivWhiteSpaceStyled = styled.div`
     white-space: pre;
@@ -106,6 +107,7 @@ export default class ViewAllPolicies extends React.Component {
         jsonEditorForPolicy: new Map(),
         showSuccessAlert: false,
         showFailAlert: false,
+        showFileSelector: false,
         policyColumnsDefinition: [
             {
                 title: "Policy Name", field: "name",
@@ -196,6 +198,8 @@ export default class ViewAllPolicies extends React.Component {
         this.generateAdditionalPolicyColumns = this.generateAdditionalPolicyColumns.bind(this);
         this.filterPolicies = this.filterPolicies.bind(this);
         this.filterTosca = this.filterTosca.bind(this);
+        this.showFileSelector = this.showFileSelector.bind(this);
+        this.disableFileSelector = this.disableFileSelector.bind(this);
         this.getAllPolicies();
         this.getAllToscaModels();
     }
@@ -263,8 +267,8 @@ export default class ViewAllPolicies extends React.Component {
                         showSuccessAlert: true,
                         showMessage: 'Policy successfully Deleted'
                     });
+                    this.getAllPolicies();
                 }
-                this.getAllPolicies();
             }
         )
     }
@@ -281,6 +285,14 @@ export default class ViewAllPolicies extends React.Component {
         this.setState({toscaModelsListDataFiltered: this.state.toscaModelsListData.filter(element => element.policyModelType.startsWith(prefixForFiltering))});
     }
 
+    showFileSelector() {
+        this.setState({showFileSelector:true});
+    }
+
+    disableFileSelector() {
+        this.setState({showFileSelector:false});
+    }
+
     renderPoliciesTab() {
         return (
                 <Tab eventKey="policies" title="Policies in Policy Framework">
@@ -343,7 +355,7 @@ export default class ViewAllPolicies extends React.Component {
                                   ]}
                                   actions={[
                                       {
-                                        icon: forwardRef((props, ref) => <DeleteRoundedIcon {...props} ref={ref} />),
+                                        icon: DeleteRoundedIcon,
                                         tooltip: 'Delete Policy',
                                         onClick: (event, rowData) => this.handleDeletePolicy(event, rowData)
                                       }
@@ -377,6 +389,14 @@ export default class ViewAllPolicies extends React.Component {
                                       headerStyle:rowHeaderStyle,
                                       actionsColumnIndex: -1
                                   }}
+                                  actions={[
+                                      {
+                                          icon: AddIcon,
+                                          tooltip: 'Add New Tosca Model',
+                                          isFreeAction: true,
+                                          onClick: () => this.showFileSelector()
+                                      }
+                                  ]}
                                   detailPanel={[
                                     {
                                       icon: ArrowForwardIosIcon,
@@ -423,7 +443,8 @@ export default class ViewAllPolicies extends React.Component {
     }
 
     render() {
-    return (
+        return (
+        <React.Fragment>
             <ModalStyled size="xl" show={this.state.show} onHide={this.handleClose} backdrop="static" keyboard={false}>
                 <Modal.Header closeButton>
                 </Modal.Header>
@@ -445,6 +466,8 @@ export default class ViewAllPolicies extends React.Component {
                     <Button variant="secondary" onClick={this.handleClose}>Close</Button>
                </Modal.Footer>
             </ModalStyled>
+            <PolicyToscaFileSelector show={this.state.showFileSelector} disableFunction={this.disableFileSelector} toscaTableUpdateFunction={this.getAllToscaModels}/>
+            </React.Fragment>
       );
     }
   }
\ No newline at end of file