From 1e2760ea1ee4baa9db0e921907f3491fda795136 Mon Sep 17 00:00:00 2001 From: sebdet Date: Fri, 26 Feb 2021 19:14:03 +0100 Subject: Introduce a new json editor component Add this a new react component so that the json editor can be called within the policies table + Add the tosca tab + Fix for Submit operation in clamp-api-v2.xml Issue-ID: POLICY-3106 Issue-ID: POLICY-3124 Signed-off-by: sebdet Change-Id: I18ab3a6034cac719525774f11b2c17f0a14bc2aa Signed-off-by: sebdet --- .../src/components/dialogs/Policy/PolicyEditor.js | 210 ++++++++++++++ .../src/components/dialogs/Policy/ToscaViewer.js | 67 +++++ .../components/dialogs/Policy/ViewAllPolicies.js | 302 +++++++++++---------- 3 files changed, 442 insertions(+), 137 deletions(-) create mode 100644 ui-react/src/components/dialogs/Policy/PolicyEditor.js create mode 100644 ui-react/src/components/dialogs/Policy/ToscaViewer.js (limited to 'ui-react/src/components') diff --git a/ui-react/src/components/dialogs/Policy/PolicyEditor.js b/ui-react/src/components/dialogs/Policy/PolicyEditor.js new file mode 100644 index 000000000..e329e0c32 --- /dev/null +++ b/ui-react/src/components/dialogs/Policy/PolicyEditor.js @@ -0,0 +1,210 @@ +/*- + * ============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 PolicyToscaService from '../../../api/PolicyToscaService'; +import JSONEditor from '@json-editor/json-editor'; +import styled from 'styled-components'; +import Button from 'react-bootstrap/Button'; +import TextField from '@material-ui/core/TextField'; +import Alert from 'react-bootstrap/Alert'; +import PolicyService from '../../../api/PolicyService'; +import OnapUtils from '../../../utils/OnapUtils'; +import uuid from 'react-uuid'; + +const DivWhiteSpaceStyled = styled.div` + white-space: pre; +` + +const JsonEditorDiv = styled.div` + margin-top: 20px; + background-color: ${props => props.theme.loopViewerBackgroundColor}; + text-align: justify; + font-size: ${props => props.theme.policyEditorFontSize}; +` +const PanelDiv = styled.div` + margin-top: 20px; + text-align: justify; + font-size: ${props => props.theme.policyEditorFontSize}; + background-color: ${props => props.theme.loopViewerBackgroundColor}; +` + +export default class PolicyEditor extends React.Component { + + state = { + policyModelType: this.props.policyModelType, + policyModelTypeVersion: this.props.policyModelTypeVersion, + policyName: (typeof this.props.policyName !== "undefined") ? this.props.policyName : "org.onap.policy.new", + policyVersion: (typeof this.props.policyVersion !== "undefined") ? this.props.policyVersion : "0.0.1", + policyProperties: this.props.policyProperties, + showSuccessAlert: false, + showFailAlert: false, + jsonEditor: null, + jsonEditorDivId: uuid(), + } + + constructor(props, context) { + super(props, context); + this.createJsonEditor = this.createJsonEditor.bind(this); + this.getToscaModelForPolicy = this.getToscaModelForPolicy.bind(this); + this.disableAlert = this.disableAlert.bind(this); + this.handleCreateNewVersion = this.handleCreateNewVersion.bind(this); + this.handleChangePolicyName = this.handleChangePolicyName.bind(this); + this.handleChangePolicyVersion = this.handleChangePolicyVersion.bind(this); + } + + disableAlert() { + this.setState ({ showSuccessAlert: false, showFailAlert: false }); + } + + customValidation(editorData) { + // method for sub-classes to override with customized validation + return []; + } + + handleCreateNewVersion() { + var editorData = this.state.jsonEditor.getValue(); + var errors = this.state.jsonEditor.validate(); + errors = errors.concat(this.customValidation(editorData)); + + if (errors.length !== 0) { + console.error("Errors detected during policy data validation ", errors); + this.setState({ + showFailAlert: true, + showMessage: 'Errors detected during policy data validation:\n' + OnapUtils.jsonEditorErrorFormatter(errors) + }); + return; + } else { + console.info("NO validation errors found in policy data"); + PolicyService.createNewPolicy(this.state.policyModelType, this.state.policyModelTypeVersion, + this.state.policyName, this.state.policyVersion, editorData).then(respPolicyCreation => { + if (respPolicyCreation === "") { + //it indicates a failure + this.setState({ + showFailAlert: true, + showMessage: 'Policy Creation Failure' + }); + } else { + this.setState({ + showSuccessAlert: true, + showMessage: 'Policy '+ this.state.policyName + '/' + this.state.policyVersion + ' created successfully' + }); + } + }) + } + } + + bumpVersion(versionToBump) { + let semVer = versionToBump.split("."); + return parseInt(semVer[0])+1 + "." + semVer[1] + "." + semVer[2]; + } + + getToscaModelForPolicy() { + PolicyToscaService.getToscaPolicyModel(this.state.policyModelType, this.state.policyModelTypeVersion).then(respJsonPolicyTosca => { + if (respJsonPolicyTosca !== {}) { + this.setState({ + jsonSchemaPolicyTosca: respJsonPolicyTosca, + jsonEditor: this.createJsonEditor(respJsonPolicyTosca, this.state.policyProperties), + }) + } + }); + } + + componentDidMount() { + this.getToscaModelForPolicy(); + } + + createJsonEditor(toscaModel, editorData) { + JSONEditor.defaults.themes.myBootstrap4 = JSONEditor.defaults.themes.bootstrap4.extend({ + getTab: function(text,tabId) { + var liel = document.createElement('li'); + liel.classList.add('nav-item'); + var ael = document.createElement("a"); + ael.classList.add("nav-link"); + ael.setAttribute("style",'padding:10px;max-width:160px;'); + ael.setAttribute("href", "#" + tabId); + ael.setAttribute('data-toggle', 'tab'); + text.setAttribute("style",'word-wrap:break-word;'); + ael.appendChild(text); + liel.appendChild(ael); + return liel; + } + }); + return new JSONEditor(document.getElementById(this.state.jsonEditorDivId), + { + schema: toscaModel, + startval: editorData, + theme: 'myBootstrap4', + object_layout: 'grid', + disable_properties: false, + disable_edit_json: false, + disable_array_reorder: true, + disable_array_delete_last_row: true, + disable_array_delete_all_rows: false, + array_controls_top: true, + keep_oneof_values: false, + collapsed:true, + show_errors: 'always', + display_required_only: false, + show_opt_in: false, + prompt_before_delete: true, + required_by_default: false + }) + } + + handleChangePolicyName(event) { + this.setState({ + policyName: event.target.value, + }); + } + + handleChangePolicyVersion(event) { + this.setState({ + policyVersion: event.target.value, + }); + } + + render() { + return ( + + + + {this.state.showMessage} + + + + + {this.state.showMessage} + + + + + + + + ); + } +} \ No newline at end of file diff --git a/ui-react/src/components/dialogs/Policy/ToscaViewer.js b/ui-react/src/components/dialogs/Policy/ToscaViewer.js new file mode 100644 index 000000000..06e83164d --- /dev/null +++ b/ui-react/src/components/dialogs/Policy/ToscaViewer.js @@ -0,0 +1,67 @@ +/*- + * ============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 PolicyToscaService from '../../../api/PolicyToscaService'; +import styled from 'styled-components'; +import Button from 'react-bootstrap/Button'; + +const JsonEditorDiv = styled.div` + margin-top: 20px; + background-color: ${props => props.theme.toscaTextareaBackgroundColor}; + text-align: justify; + font-size: ${props => props.theme.toscaTextareaFontSize}; + width: 100%; + height: 30%; +` + +export default class ToscaViewer extends React.Component { + + state = { + toscaData: this.props.toscaData, + yamlPolicyTosca: this.getToscaModelYamlFor(this.props.toscaData), + } + + constructor(props, context) { + super(props, context); + this.getToscaModelYamlFor = this.getToscaModelYamlFor.bind(this); + } + + getToscaModelYamlFor(toscaData) { + PolicyToscaService.getToscaPolicyModelYaml(toscaData["policyModelType"], toscaData["version"]).then(respYamlPolicyTosca => { + this.setState({ + yamlPolicyTosca: respYamlPolicyTosca, + }) + }); + } + + render() { + return ( + +
{this.state.yamlPolicyTosca}
+ +
+ ); + } +} \ No newline at end of file diff --git a/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js b/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js index b159584dd..1e71bb853 100644 --- a/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js +++ b/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js @@ -31,7 +31,7 @@ import Check from '@material-ui/icons/Check'; import ChevronLeft from '@material-ui/icons/ChevronLeft'; import ChevronRight from '@material-ui/icons/ChevronRight'; import Clear from '@material-ui/icons/Clear'; -import DeleteOutline from '@material-ui/icons/DeleteOutline'; +import DeleteRoundedIcon from '@material-ui/icons/DeleteRounded'; import Edit from '@material-ui/icons/Edit'; import FilterList from '@material-ui/icons/FilterList'; import FirstPage from '@material-ui/icons/FirstPage'; @@ -40,42 +40,44 @@ import Remove from '@material-ui/icons/Remove'; import SaveAlt from '@material-ui/icons/SaveAlt'; import Search from '@material-ui/icons/Search'; import ViewColumn from '@material-ui/icons/ViewColumn'; +import DescriptionIcon from '@material-ui/icons/Description'; +import SettingsEthernetIcon from '@material-ui/icons/SettingsEthernet'; +import NoteAddIcon from '@material-ui/icons/NoteAdd'; import FormControlLabel from '@material-ui/core/FormControlLabel'; import Switch from '@material-ui/core/Switch'; import MaterialTable from "material-table"; import PolicyService from '../../../api/PolicyService'; import PolicyToscaService from '../../../api/PolicyToscaService'; import Select from 'react-select'; -import JSONEditor from '@json-editor/json-editor'; -import OnapUtils from '../../../utils/OnapUtils'; import Alert from 'react-bootstrap/Alert'; import Tabs from 'react-bootstrap/Tabs'; import Tab from 'react-bootstrap/Tab'; +import PolicyEditor from './PolicyEditor'; +import ToscaViewer from './ToscaViewer'; const DivWhiteSpaceStyled = styled.div` white-space: pre; ` const ModalStyled = styled(Modal)` - @media (min-width: 1200px) { + @media (min-width: 1000px) { .modal-xl { max-width: 96%; } } background-color: transparent; ` -const JsonEditorDiv = styled.div` - margin-top: 20px; - background-color: ${props => props.theme.toscaTextareaBackgroundColor}; - text-align: justify; - font-size: ${props => props.theme.toscaTextareaFontSize}; - width: 100%; - height: 30%; - border: 1px solid black; +const DetailedRow = styled.div` + margin: 0 auto; + background-color: ${props => props.theme.policyEditorBackgroundColor}; + font-size: ${props => props.theme.policyEditorFontSize}; + width: 97%; + margin-left: auto; + margin-right: 0; ` -const standardCellStyle = { border: '1px solid black' }; +const standardCellStyle = { backgroundColor: '#039be5', color: '#FFF', border: '1px solid black' }; const cellPdpGroupStyle = { backgroundColor: '#039be5', color: '#FFF', border: '1px solid black'}; const headerStyle = { backgroundColor: '#ddd', border: '2px solid black' }; const rowHeaderStyle = {backgroundColor:'#ddd', fontSize: '15pt', text: 'bold', border: '1px solid black'}; @@ -86,6 +88,8 @@ export default class ViewAllPolicies extends React.Component { content: 'Please select a policy to display it', selectedRowId: -1, policiesListData: [], + toscaModelsListData: [], + jsonEditorForPolicy: new Map(), prefixGrouping: false, showSuccessAlert: false, showFailAlert: false, @@ -128,11 +132,38 @@ export default class ViewAllPolicies extends React.Component { headerStyle: headerStyle } ], + toscaColumnsDefinition: [ + { + title: "Policy Model Type", field: "policyModelType", + cellStyle: standardCellStyle, + headerStyle: headerStyle + }, + { + title: "Policy Acronym", field: "policyAcronym", + cellStyle: standardCellStyle, + headerStyle: headerStyle + }, + { + title: "Version", field: "version", + cellStyle: standardCellStyle, + headerStyle: headerStyle + }, + { + title: "Uploaded By", field: "updatedBy", + cellStyle: standardCellStyle, + headerStyle: headerStyle + }, + { + title: "Uploaded Date", field: "updatedDate", editable: 'never', + cellStyle: standardCellStyle, + headerStyle: headerStyle + } + ], tableIcons: { Add: forwardRef((props, ref) => ), Check: forwardRef((props, ref) => ), Clear: forwardRef((props, ref) => ), - Delete: forwardRef((props, ref) => ), + Delete: forwardRef((props, ref) => ), DetailPanel: forwardRef((props, ref) => ), Edit: forwardRef((props, ref) => ), Export: forwardRef((props, ref) => ), @@ -154,14 +185,17 @@ export default class ViewAllPolicies extends React.Component { this.handleClose = this.handleClose.bind(this); this.renderPdpGroupDropBox = this.renderPdpGroupDropBox.bind(this); this.handlePdpGroupChange = this.handlePdpGroupChange.bind(this); - this.createJsonEditor = this.createJsonEditor.bind(this); this.handlePrefixGrouping = this.handlePrefixGrouping.bind(this); this.handleDeletePolicy = this.handleDeletePolicy.bind(this); - this.handleUpdatePolicy = this.handleUpdatePolicy.bind(this); - this.handleCreateNewVersion = this.handleCreateNewVersion.bind(this); this.disableAlert = this.disableAlert.bind(this); this.getAllPolicies(); + this.getAllToscaModels(); + } + getAllToscaModels() { + PolicyToscaService.getToscaPolicyModels().then(toscaModelsList => { + this.setState({ toscaModelsListData: toscaModelsList }); + }); } handlePdpGroupChange(e) { @@ -177,45 +211,6 @@ export default class ViewAllPolicies extends React.Component { } } - createJsonEditor(toscaModel, editorData) { - document.getElementById("policy-editor").innerHTML = ""; - JSONEditor.defaults.themes.myBootstrap4 = JSONEditor.defaults.themes.bootstrap4.extend({ - getTab: function(text,tabId) { - var liel = document.createElement('li'); - liel.classList.add('nav-item'); - var ael = document.createElement("a"); - ael.classList.add("nav-link"); - ael.setAttribute("style",'padding:10px;max-width:160px;'); - ael.setAttribute("href", "#" + tabId); - ael.setAttribute('data-toggle', 'tab'); - text.setAttribute("style",'word-wrap:break-word;'); - ael.appendChild(text); - liel.appendChild(ael); - return liel; - } - }); - return new JSONEditor(document.getElementById("policy-editor"), - { - schema: toscaModel, - startval: editorData, - theme: 'myBootstrap4', - object_layout: 'grid', - disable_properties: false, - disable_edit_json: false, - disable_array_reorder: true, - disable_array_delete_last_row: true, - disable_array_delete_all_rows: false, - array_controls_top: true, - keep_oneof_values: false, - collapsed:true, - show_errors: 'always', - display_required_only: false, - show_opt_in: false, - prompt_before_delete: true, - required_by_default: false - }) - } - renderPdpGroupDropBox(dataRow) { let optionItems = [{label: "NOT DEPLOYED", value: "NOT DEPLOYED"}]; let selectedItem = {label: "NOT DEPLOYED", value: "NOT DEPLOYED"}; @@ -245,17 +240,6 @@ export default class ViewAllPolicies extends React.Component { this.props.history.push('/') } - handleOnRowClick(rowData) { - PolicyToscaService.getToscaPolicyModel(rowData["type"], rowData["type_version"]).then(respJsonPolicyTosca => { - this.setState({ - selectedRowId: rowData.tableData.id, - selectedRowIdJsonSchema: respJsonPolicyTosca, - selectedRowIdPolicyProperties: rowData["properties"], - jsonEditorForPolicy: this.createJsonEditor(respJsonPolicyTosca, rowData["properties"]) - }); - }); - } - handlePrefixGrouping(event) { this.setState({prefixGrouping: event.target.checked}); } @@ -279,92 +263,60 @@ export default class ViewAllPolicies extends React.Component { ) } - customValidation(editorData) { - // method for sub-classes to override with customized validation - return []; - } - - handleCreateNewVersion() { - var editorData = this.state.jsonEditorForPolicy.getValue(); - var errors = this.state.jsonEditorForPolicy.validate(); - errors = errors.concat(this.customValidation(editorData)); - - if (errors.length !== 0) { - console.error("Errors detected during policy data validation ", errors); - this.setState({ - showFailAlert: true, - showMessage: 'Errors detected during policy data validation:\n' + OnapUtils.jsonEditorErrorFormatter(errors) - }); - return; - } else { - console.info("NO validation errors found in policy data"); - let newPolicy = JSON.parse(JSON.stringify(this.state.policiesListData[this.state.selectedRowId])); - newPolicy["properties"] = editorData; - let newVersion = this.bumpVersion(newPolicy["version"]); - newPolicy["version"] = newVersion; - newPolicy["metadata"]["policy-version"] = newVersion; - // Remove stuff added by UI - delete newPolicy["tableData"]; - PolicyService.createNewPolicy(newPolicy["type"], newPolicy["type_version"], newPolicy).then(respPolicyCreation => { - if (respPolicyCreation === "") { - //it indicates a failure - this.setState({ - showFailAlert: true, - showMessage: 'Policy Creation Failure' - }); - } else { - this.setState({ - showSuccessAlert: true, - showMessage: 'Policy in version ' + newVersion + ' created successfully' - }); - } - }) - } - } - - bumpVersion(versionToBump) { - let semVer = versionToBump.split("."); - return parseInt(semVer[0])+1 + "." + semVer[1] + "." + semVer[2]; - } - - handleUpdatePolicy() { - this.setState({ show: false }); - this.props.history.push('/') - } - disableAlert() { this.setState ({ showSuccessAlert: false, showFailAlert: false }); } - render() { - return ( - - - - this.setState({ key, selectedRowData: {} })}> - + renderPoliciesTab() { + return ( + } label="Group by prefix" /> {this.handleOnRowClick(rowData)}} + onRowClick={(event, rowData, togglePanel) => togglePanel()} options={{ grouping: true, exportButton: true, headerStyle:rowHeaderStyle, rowStyle: rowData => ({ backgroundColor: (this.state.selectedRowId !== -1 && this.state.selectedRowId === rowData.tableData.id) ? '#EEE' : '#FFF' - }) + }), + actionsColumnIndex: -1 }} + detailPanel={[ + { + icon: SettingsEthernetIcon, + tooltip: 'Show Configuration', + render: rowData => { + return ( + + + + ) + }, + }, + { + icon: DescriptionIcon, + tooltip: 'Show Raw Data', + render: rowData => { + return ( + +
{JSON.stringify(rowData, null, 2)}
+
+ ) + }, + }, + ]} actions={[ { - icon: forwardRef((props, ref) => ), + icon: forwardRef((props, ref) => ), tooltip: 'Delete Policy', onClick: (event, rowData) => this.handleDeletePolicy(event, rowData) } @@ -372,13 +324,89 @@ export default class ViewAllPolicies extends React.Component { />
+ ); + } + + renderToscaTab() { + return ( + + + } + label="Group by prefix" + /> + togglePanel()} + options={{ + grouping: true, + exportButton: true, + headerStyle:rowHeaderStyle, + rowStyle: rowData => ({ + backgroundColor: (this.state.selectedRowId !== -1 && this.state.selectedRowId === rowData.tableData.id) ? '#EEE' : '#FFF' + }), + actionsColumnIndex: -1 + }} + detailPanel={[ + { + icon: SettingsEthernetIcon, + tooltip: 'Show Tosca', + render: rowData => { + return ( + + + + ) + }, + }, + { + icon: DescriptionIcon, + tooltip: 'Show Raw Data', + render: rowData => { + return ( + +
{JSON.stringify(rowData, null, 2)}
+
+ ) + }, + }, + { + icon: NoteAddIcon, + tooltip: 'Create a policy from this model', + render: rowData => { + return ( + + + + ) + }, + }, + ]} + actions={[ + { + icon: forwardRef((props, ref) => ), + tooltip: 'Delete Tosca Model', + onClick: (event, rowData) => this.handleDeletePolicy(event, rowData) + } + ]} + /> +
+
+ ); + } + + render() { + return ( + + + + this.setState({ key, selectedRowData: {} })}> + {this.renderPoliciesTab()} + {this.renderToscaTab()} - -
Policy Properties Editor
-
- - {this.state.showMessage} -- cgit 1.2.3-korg