aboutsummaryrefslogtreecommitdiffstats
path: root/gui-clamp/ui-react/src/components/dialogs/Policy
diff options
context:
space:
mode:
authorbrunomilitzer <bruno.militzer@est.tech>2021-05-18 12:50:32 +0100
committerbrunomilitzer <bruno.militzer@est.tech>2021-05-27 09:43:12 +0100
commit87111eea8588fb30936a8f876f5f3feed61e7b8a (patch)
tree1fe70089a51b4065f9db26e643982418d3c23756 /gui-clamp/ui-react/src/components/dialogs/Policy
parente6a9251b46ce602ac0dccfe5890d886c7e445ed7 (diff)
React Front-End UI
This commit is to move the React front end files from the Clamp Policy Repo to the Onap Gui Policy Repo. Also created the added Maven featue to compile the React project via Node and NPM, and copy the production files to the target directory. Fixed in gitignore that was ignoring the contents inside the logs/ directory. Reformated JS spacing files from 4 spaces to 2 spaces. Fixed Broken JEST test Applied Jim's Code Review as well updated Node Version from 12.18 to 14.17 accross the modules. Unfortunately cannot apply NPM version since it broke npm install. Fixed ONAP Job Builder Linting Error Applied Jim's Second Code Review Issue-ID: POLICY-3218 Signed-off-by: brunomilitzer <bruno.militzer@est.tech> Change-Id: I01f95c350d27d72f941c835592fd596472601d6e
Diffstat (limited to 'gui-clamp/ui-react/src/components/dialogs/Policy')
-rw-r--r--gui-clamp/ui-react/src/components/dialogs/Policy/PolicyEditor.js217
-rw-r--r--gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.js364
-rw-r--r--gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.test.js128
-rw-r--r--gui-clamp/ui-react/src/components/dialogs/Policy/ToscaViewer.js67
-rw-r--r--gui-clamp/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js427
5 files changed, 1203 insertions, 0 deletions
diff --git a/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyEditor.js b/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyEditor.js
new file mode 100644
index 0000000..525d66b
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyEditor.js
@@ -0,0 +1,217 @@
+/*-
+ * ============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/dist/nonmin/jsoneditor.js';
+import "@fortawesome/fontawesome-free/css/all.css"
+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 JSONEditor = require("@json-editor/json-editor").JSONEditor;
+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 };
+ border: 1px solid #C0C0C0;
+`
+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 (typeof (respPolicyCreation) === "undefined") {
+ //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'
+ });
+ this.props.policyUpdateFunction();
+ }
+ })
+ }
+ }
+
+ 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',
+ theme: 'bootstrap4',
+ iconlib: 'fontawesome5',
+ 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 (
+ <PanelDiv>
+ <Alert variant="success" show={ this.state.showSuccessAlert } onClose={ this.disableAlert } dismissible>
+ <DivWhiteSpaceStyled>
+ { this.state.showMessage }
+ </DivWhiteSpaceStyled>
+ </Alert>
+ <Alert variant="danger" show={ this.state.showFailAlert } onClose={ this.disableAlert } dismissible>
+ <DivWhiteSpaceStyled>
+ { this.state.showMessage }
+ </DivWhiteSpaceStyled>
+ </Alert>
+ <TextField required id="policyName" label="Required" defaultValue={ this.state.policyName }
+ onChange={ this.handleChangePolicyName } variant="outlined" size="small"/>
+ <TextField required id="policyVersion" label="Required" defaultValue={ this.state.policyVersion }
+ onChange={ this.handleChangePolicyVersion } size="small" variant="outlined"/>
+ <Button variant="secondary" title="Create a new policy version from the defined parameters"
+ onClick={ this.handleCreateNewVersion }>Create New Version</Button>
+ <JsonEditorDiv id={ this.state.jsonEditorDivId } title="Policy Properties"/>
+ </PanelDiv>
+ );
+ }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.js b/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.js
new file mode 100644
index 0000000..105b2c5
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.js
@@ -0,0 +1,364 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * ONAP POLICY-CLAMP
+ * ================================================================================
+ * Copyright (C) 2020-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 Button from 'react-bootstrap/Button';
+import Form from 'react-bootstrap/Form';
+import Col from 'react-bootstrap/Col';
+import Row from 'react-bootstrap/Row';
+import Select from 'react-select';
+import Modal from 'react-bootstrap/Modal';
+import styled from 'styled-components';
+import LoopService from '../../../api/LoopService';
+import LoopCache from '../../../api/LoopCache';
+import { JSONEditor } from '@json-editor/json-editor/dist/jsoneditor.js';
+import "@fortawesome/fontawesome-free/css/all.css"
+import Alert from 'react-bootstrap/Alert';
+import OnapConstant from '../../../utils/OnapConstants';
+import OnapUtils from '../../../utils/OnapUtils';
+
+const ModalStyled = styled(Modal)`
+ background-color: transparent;
+`
+
+const DivWhiteSpaceStyled = styled.div`
+ white-space: pre;
+`
+
+export default class PolicyModal extends React.Component {
+
+ state = {
+ show: true,
+ loopCache: this.props.loopCache,
+ jsonEditor: null,
+ policyName: this.props.match.params.policyName,
+ // This is to indicate whether it's an operational or config policy (in terms of loop instance)
+ policyInstanceType: this.props.match.params.policyInstanceType,
+ pdpGroup: null,
+ pdpGroupList: [],
+ pdpSubgroupList: [],
+ chosenPdpGroup: '',
+ chosenPdpSubgroup: '',
+ showSucAlert: false,
+ showFailAlert: false
+ };
+
+ 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.handlePdpGroupChange = this.handlePdpGroupChange.bind(this);
+ this.handlePdpSubgroupChange = this.handlePdpSubgroupChange.bind(this);
+ this.createJsonEditor = this.createJsonEditor.bind(this);
+ this.handleRefresh = this.handleRefresh.bind(this);
+ this.disableAlert = this.disableAlert.bind(this);
+ this.renderPdpGroupDropDown = this.renderPdpGroupDropDown.bind(this);
+ this.renderOpenLoopMessage = this.renderOpenLoopMessage.bind(this);
+ this.renderModalTitle = this.renderModalTitle.bind(this);
+ this.readOnly = props.readOnly !== undefined ? props.readOnly : false;
+ }
+
+ handleSave() {
+ var editorData = this.state.jsonEditor.getValue();
+ var errors = this.state.jsonEditor.validate();
+ errors = errors.concat(this.customValidation(editorData, this.state.loopCache.getTemplateName()));
+
+ 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");
+ if (this.state.policyInstanceType === OnapConstant.microServiceType) {
+ this.state.loopCache.updateMicroServiceProperties(this.state.policyName, editorData);
+ this.state.loopCache.updateMicroServicePdpGroup(this.state.policyName, this.state.chosenPdpGroup, this.state.chosenPdpSubgroup);
+ LoopService.setMicroServiceProperties(this.state.loopCache.getLoopName(), this.state.loopCache.getMicroServiceForName(this.state.policyName)).then(resp => {
+ this.setState({ show: false });
+ this.props.history.push('/');
+ this.props.loadLoopFunction(this.state.loopCache.getLoopName());
+ });
+ } else if (this.state.policyInstanceType === OnapConstant.operationalPolicyType) {
+ this.state.loopCache.updateOperationalPolicyProperties(this.state.policyName, editorData);
+ this.state.loopCache.updateOperationalPolicyPdpGroup(this.state.policyName, this.state.chosenPdpGroup, this.state.chosenPdpSubgroup);
+ LoopService.setOperationalPolicyProperties(this.state.loopCache.getLoopName(), this.state.loopCache.getOperationalPolicies()).then(resp => {
+ this.setState({ show: false });
+ this.props.history.push('/');
+ this.props.loadLoopFunction(this.state.loopCache.getLoopName());
+ });
+ }
+ }
+ }
+
+ customValidation(editorData, templateName) {
+ // method for sub-classes to override with customized validation
+ return [];
+ }
+
+ handleClose() {
+ this.setState({ show: false });
+ this.props.history.push('/');
+ }
+
+ componentDidMount() {
+ this.renderJsonEditor();
+ }
+
+ componentDidUpdate() {
+ if (this.state.showSucAlert === true || this.state.showFailAlert === true) {
+ let modalElement = document.getElementById("policyModal")
+ if (modalElement) {
+ modalElement.scrollTo(0, 0);
+ }
+ }
+ }
+
+ 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("editor"),
+ {
+ schema: toscaModel,
+ startval: editorData,
+ theme: 'bootstrap4',
+ iconlib: 'fontawesome5',
+ 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
+ })
+ }
+
+ renderJsonEditor() {
+ console.debug("Rendering PolicyModal ", this.state.policyName);
+ var toscaModel = {};
+ var editorData = {};
+ var pdpGroupValues = {};
+ var chosenPdpGroupValue, chosenPdpSubgroupValue;
+ if (this.state.policyInstanceType === OnapConstant.microServiceType) {
+ toscaModel = this.state.loopCache.getMicroServiceJsonRepresentationForName(this.state.policyName);
+ editorData = this.state.loopCache.getMicroServicePropertiesForName(this.state.policyName);
+ pdpGroupValues = this.state.loopCache.getMicroServiceSupportedPdpGroup(this.state.policyName);
+ chosenPdpGroupValue = this.state.loopCache.getMicroServicePdpGroup(this.state.policyName);
+ chosenPdpSubgroupValue = this.state.loopCache.getMicroServicePdpSubgroup(this.state.policyName);
+ } else if (this.state.policyInstanceType === OnapConstant.operationalPolicyType) {
+ toscaModel = this.state.loopCache.getOperationalPolicyJsonRepresentationForName(this.state.policyName);
+ editorData = this.state.loopCache.getOperationalPolicyPropertiesForName(this.state.policyName);
+ pdpGroupValues = this.state.loopCache.getOperationalPolicySupportedPdpGroup(this.state.policyName);
+ chosenPdpGroupValue = this.state.loopCache.getOperationalPolicyPdpGroup(this.state.policyName);
+ chosenPdpSubgroupValue = this.state.loopCache.getOperationalPolicyPdpSubgroup(this.state.policyName);
+ }
+
+ if (toscaModel == null) {
+ return;
+ }
+
+ var pdpSubgroupValues = [];
+ if (typeof (chosenPdpGroupValue) !== "undefined") {
+ var selectedPdpGroup = pdpGroupValues.filter(entry => (Object.keys(entry)[0] === chosenPdpGroupValue));
+ pdpSubgroupValues = selectedPdpGroup[0][chosenPdpGroupValue].map((pdpSubgroup) => {
+ return { label: pdpSubgroup, value: pdpSubgroup }
+ });
+ }
+ this.setState({
+ jsonEditor: this.createJsonEditor(toscaModel, editorData),
+ pdpGroup: pdpGroupValues,
+ pdpGroupList: pdpGroupValues.map(entry => {
+ return { label: Object.keys(entry)[0], value: Object.keys(entry)[0] };
+ }),
+ pdpSubgroupList: pdpSubgroupValues,
+ chosenPdpGroup: chosenPdpGroupValue,
+ chosenPdpSubgroup: chosenPdpSubgroupValue
+ })
+ }
+
+ handlePdpGroupChange(e) {
+ var selectedPdpGroup = this.state.pdpGroup.filter(entry => (Object.keys(entry)[0] === e.value));
+ const pdpSubgroupValues = selectedPdpGroup[0][e.value].map((pdpSubgroup) => {
+ return { label: pdpSubgroup, value: pdpSubgroup }
+ });
+ if (this.state.chosenPdpGroup !== e.value) {
+ this.setState({
+ chosenPdpGroup: e.value,
+ chosenPdpSubgroup: '',
+ pdpSubgroupList: pdpSubgroupValues
+ });
+ }
+ }
+
+ handlePdpSubgroupChange(e) {
+ this.setState({ chosenPdpSubgroup: e.value });
+ }
+
+ handleRefresh() {
+ var newLoopCache, toscaModel, editorData;
+ if (this.state.policyInstanceType === OnapConstant.microServiceType) {
+ LoopService.refreshMicroServicePolicyJson(this.state.loopCache.getLoopName(), this.state.policyName).then(data => {
+ newLoopCache = new LoopCache(data);
+ toscaModel = newLoopCache.getMicroServiceJsonRepresentationForName(this.state.policyName);
+ editorData = newLoopCache.getMicroServicePropertiesForName(this.state.policyName);
+ document.getElementById("editor").innerHTML = "";
+ this.setState({
+ loopCache: newLoopCache,
+ jsonEditor: this.createJsonEditor(toscaModel, editorData),
+ showSucAlert: true,
+ showMessage: "Successfully refreshed"
+ });
+ })
+ .catch(error => {
+ console.error("Error while refreshing the Operational Policy Json Representation");
+ this.setState({
+ showFailAlert: true,
+ showMessage: "Refreshing of UI failed"
+ });
+ });
+ } else if (this.state.policyInstanceType === OnapConstant.operationalPolicyType) {
+ LoopService.refreshOperationalPolicyJson(this.state.loopCache.getLoopName(), this.state.policyName).then(data => {
+ var newLoopCache = new LoopCache(data);
+ toscaModel = newLoopCache.getOperationalPolicyJsonRepresentationForName(this.state.policyName);
+ editorData = newLoopCache.getOperationalPolicyPropertiesForName(this.state.policyName);
+ document.getElementById("editor").innerHTML = "";
+ this.setState({
+ loopCache: newLoopCache,
+ jsonEditor: this.createJsonEditor(toscaModel, editorData),
+ showSucAlert: true,
+ showMessage: "Successfully refreshed"
+ });
+ })
+ .catch(error => {
+ console.error("Error while refreshing the Operational Policy Json Representation");
+ this.setState({
+ showFailAlert: true,
+ showMessage: "Refreshing of UI failed"
+ });
+ });
+ }
+ }
+
+ disableAlert() {
+ this.setState({ showSucAlert: false, showFailAlert: false });
+ }
+
+ renderPdpGroupDropDown() {
+ if (this.state.policyInstanceType !== OnapConstant.operationalPolicyType || !this.state.loopCache.isOpenLoopTemplate()) {
+ return (
+ <Form.Group as={ Row } controlId="formPlaintextEmail">
+ <Form.Label column sm="2">Pdp Group Info</Form.Label>
+ <Col sm="3">
+ <Select value={ { label: this.state.chosenPdpGroup, value: this.state.chosenPdpGroup } } onChange={ this.handlePdpGroupChange } options={ this.state.pdpGroupList }/>
+ </Col>
+ <Col sm="3">
+ <Select value={ { label: this.state.chosenPdpSubgroup, value: this.state.chosenPdpSubgroup } } onChange={ this.handlePdpSubgroupChange } options={ this.state.pdpSubgroupList }/>
+ </Col>
+ </Form.Group>
+ );
+ }
+ }
+
+ renderOpenLoopMessage() {
+ if (this.state.policyInstanceType === OnapConstant.operationalPolicyType && this.state.loopCache.isOpenLoopTemplate()) {
+ return (
+ "Operational Policy cannot be configured as only Open Loop is supported for this Template!"
+ );
+ }
+ }
+
+ renderModalTitle() {
+ return (
+ <Modal.Title>Edit the policy</Modal.Title>
+ );
+ }
+
+ renderButton() {
+ var allElement = [(<Button variant="secondary" onClick={ this.handleClose }>
+ Close
+ </Button>)];
+ if (this.state.policyInstanceType !== OnapConstant.operationalPolicyType || !this.state.loopCache.isOpenLoopTemplate()) {
+ allElement.push((
+ <Button variant="primary" disabled={ this.readOnly } onClick={ this.handleSave }>
+ Save Changes
+ </Button>
+ ));
+ allElement.push((
+ <Button variant="primary" disabled={ this.readOnly } onClick={ this.handleRefresh }>
+ Refresh
+ </Button>
+ ));
+ }
+ return allElement;
+ }
+
+ render() {
+ return (
+ <ModalStyled size="xl" backdrop="static" keyboard={ false } show={ this.state.show } onHide={ this.handleClose }>
+ <Modal.Header closeButton>
+ { this.renderModalTitle() }
+ </Modal.Header>
+ <Alert variant="success" show={ this.state.showSucAlert } onClose={ this.disableAlert } dismissible>
+ <DivWhiteSpaceStyled>
+ { this.state.showMessage }
+ </DivWhiteSpaceStyled>
+ </Alert>
+ <Alert variant="danger" show={ this.state.showFailAlert } onClose={ this.disableAlert } dismissible>
+ <DivWhiteSpaceStyled>
+ { this.state.showMessage }
+ </DivWhiteSpaceStyled>
+ </Alert>
+ <Modal.Body>
+ { this.renderOpenLoopMessage() }
+ <div id="editor"/>
+ { this.renderPdpGroupDropDown() }
+ </Modal.Body>
+ <Modal.Footer>
+ { this.renderButton() }
+ </Modal.Footer>
+ </ModalStyled>
+ );
+ }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.test.js b/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.test.js
new file mode 100644
index 0000000..734f22e
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Policy/PolicyModal.test.js
@@ -0,0 +1,128 @@
+/*-
+ * ============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 PolicyModal from './PolicyModal';
+import LoopCache from '../../../api/LoopCache';
+import LoopService from '../../../api/LoopService';
+import OnapConstant from '../../../utils/OnapConstants';
+
+describe('Verify PolicyModal', () => {
+ beforeEach(() => {
+ fetch.resetMocks();
+ fetch.mockImplementation(() => {
+ return Promise.resolve({
+ ok: true,
+ status: 200,
+ text: () => "OK"
+ });
+ });
+ })
+ const loopCacheStr = {
+ "name": "LOOP_Jbv1z_v1_0_ResourceInstanceName1_tca",
+ "operationalPolicies": [{
+ "name": "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca",
+ "configurationsJson": {
+ "operational_policy": {
+ "controlLoop": {},
+ "policies": []
+ }
+ },
+ "policyModel": { "policyPdpGroup": { "supportedPdpGroups": [{ "monitoring": ["xacml"] }] } },
+ "jsonRepresentation": { "schema": {} }
+ }]
+ };
+ const loopCache = new LoopCache(loopCacheStr);
+ const historyMock = { push: jest.fn() };
+ const flushPromises = () => new Promise(setImmediate);
+ const match = { params: { policyName: "OPERATIONAL_h2NMX_v1_0_ResourceInstanceName1_tca", policyInstanceType: OnapConstant.operationalPolicyType } }
+
+ it('Test handleClose', () => {
+ const handleClose = jest.spyOn(PolicyModal.prototype, 'handleClose');
+ const component = mount(<PolicyModal history={ historyMock } match={ match } loopCache={ loopCache }/>)
+
+ component.find('[variant="secondary"]').prop('onClick')();
+
+ expect(handleClose).toHaveBeenCalledTimes(1);
+ expect(component.state('show')).toEqual(false);
+ expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+ });
+
+ it('Test handleSave', async () => {
+ const loadLoopFunction = jest.fn();
+ const handleSave = jest.spyOn(PolicyModal.prototype, 'handleSave');
+ const component = mount(<PolicyModal history={ historyMock }
+ loopCache={ loopCache } match={ match } loadLoopFunction={ loadLoopFunction }/>)
+
+ component.find('[variant="primary"]').get(0).props.onClick();
+ await flushPromises();
+ component.update();
+
+ expect(handleSave).toHaveBeenCalledTimes(1);
+ expect(component.state('show')).toEqual(false);
+ expect(historyMock.push.mock.calls[0]).toEqual(['/']);
+ });
+
+ it('Test handleRefresh', async () => {
+ LoopService.refreshOperationalPolicyJson = jest.fn().mockImplementation(() => {
+ return Promise.resolve(loopCacheStr);
+ });
+ const updateLoopFunction = jest.fn();
+ const handleRefresh = jest.spyOn(PolicyModal.prototype, 'handleRefresh');
+ const component = mount(<PolicyModal loopCache={ loopCache } match={ match } updateLoopFunction={ updateLoopFunction }/>)
+
+ component.find('[variant="primary"]').get(1).props.onClick();
+ await flushPromises();
+ component.update();
+
+ expect(handleRefresh).toHaveBeenCalledTimes(1);
+ expect(component.state('show')).toEqual(true);
+ expect(component.state('showSucAlert')).toEqual(true);
+ expect(component.state('showMessage')).toEqual("Successfully refreshed");
+ });
+
+ it('Test handlePdpGroupChange', () => {
+ const component = mount(<PolicyModal loopCache={ loopCache } match={ match }/>)
+ component.setState({
+ "pdpGroup": [{ "option1": ["subPdp1", "subPdp2"] }],
+ "chosenPdpGroup": "option2"
+ });
+ expect(component.state('chosenPdpGroup')).toEqual("option2");
+
+ const instance = component.instance();
+ const event = { label: "option1", value: "option1" }
+ instance.handlePdpGroupChange(event);
+ expect(component.state('chosenPdpGroup')).toEqual("option1");
+ expect(component.state('chosenPdpSubgroup')).toEqual("");
+ expect(component.state('pdpSubgroupList')).toEqual([{ label: "subPdp1", value: "subPdp1" }, { label: "subPdp2", value: "subPdp2" }]);
+ });
+
+ it('Test handlePdpSubgroupChange', () => {
+ const component = mount(<PolicyModal loopCache={ loopCache } match={ match }/>)
+
+ const instance = component.instance();
+ const event = { label: "option1", value: "option1" }
+ instance.handlePdpSubgroupChange(event);
+ expect(component.state('chosenPdpSubgroup')).toEqual("option1");
+ });
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/Policy/ToscaViewer.js b/gui-clamp/ui-react/src/components/dialogs/Policy/ToscaViewer.js
new file mode 100644
index 0000000..ce4edce
--- /dev/null
+++ b/gui-clamp/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 (
+ <JsonEditorDiv>
+ <pre>{ this.state.yamlPolicyTosca }</pre>
+ <Button variant="secondary" title="Create a new policy version from the defined parameters"
+ onClick={ this.handleCreateNewVersion }>Create New Version</Button>
+ </JsonEditorDiv>
+ );
+ }
+}
diff --git a/gui-clamp/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js b/gui-clamp/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js
new file mode 100644
index 0000000..a97deea
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/Policy/ViewAllPolicies.js
@@ -0,0 +1,427 @@
+/*-
+ * ============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, { forwardRef } from 'react'
+import Button from 'react-bootstrap/Button';
+import Modal from 'react-bootstrap/Modal';
+import styled from 'styled-components';
+import AddBox from '@material-ui/icons/AddBox';
+import ArrowDownward from '@material-ui/icons/ArrowDownward';
+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 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';
+import LastPage from '@material-ui/icons/LastPage';
+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 DehazeIcon from '@material-ui/icons/Dehaze';
+import ArrowForwardIosIcon from '@material-ui/icons/ArrowForwardIos';
+import AddIcon from '@material-ui/icons/Add';
+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 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: 1000px) {
+ .modal-xl {
+ max-width: 96%;
+ }
+ }
+ background-color: transparent;
+`
+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 = { 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' };
+
+export default class ViewAllPolicies extends React.Component {
+ state = {
+ show: true,
+ content: 'Please select a policy to display it',
+ selectedRowId: -1,
+ policiesListData: [],
+ toscaModelsListData: [],
+ jsonEditorForPolicy: new Map(),
+ prefixGrouping: false,
+ showSuccessAlert: false,
+ showFailAlert: false,
+ policyColumnsDefinition: [
+ {
+ title: "Policy Name", field: "name",
+ cellStyle: standardCellStyle,
+ headerStyle: headerStyle
+ },
+ {
+ title: "Policy Version", field: "version",
+ cellStyle: standardCellStyle,
+ headerStyle: headerStyle
+ },
+ {
+ title: "Policy Type", field: "type",
+ cellStyle: standardCellStyle,
+ headerStyle: headerStyle
+ },
+ {
+ title: "Policy Type Version", field: "type_version",
+ cellStyle: standardCellStyle,
+ headerStyle: headerStyle
+ },
+ {
+ title: "Deployed in PDP", field: "pdpGroupInfo.pdpGroup",
+ cellStyle: cellPdpGroupStyle,
+ headerStyle: headerStyle,
+ render: rowData => this.renderPdpGroupDropBox(rowData),
+ grouping: false
+ },
+ {
+ title: "PDP Group", field: "pdpGroupInfo.pdpGroup",
+ cellStyle: cellPdpGroupStyle,
+ headerStyle: headerStyle
+ },
+ {
+ title: "PDP SubGroup", field: "pdpGroupInfo.pdpSubGroup",
+ cellStyle: cellPdpGroupStyle,
+ 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) => <AddBox { ...props } ref={ ref }/>),
+ Check: forwardRef((props, ref) => <Check { ...props } ref={ ref }/>),
+ Clear: forwardRef((props, ref) => <Clear { ...props } ref={ ref }/>),
+ Delete: forwardRef((props, ref) => <DeleteRoundedIcon { ...props } ref={ ref }/>),
+ DetailPanel: forwardRef((props, ref) => <ChevronRight { ...props } ref={ ref }/>),
+ Edit: forwardRef((props, ref) => <Edit { ...props } ref={ ref }/>),
+ Export: forwardRef((props, ref) => <SaveAlt { ...props } ref={ ref }/>),
+ Filter: forwardRef((props, ref) => <FilterList { ...props } ref={ ref }/>),
+ FirstPage: forwardRef((props, ref) => <FirstPage { ...props } ref={ ref }/>),
+ LastPage: forwardRef((props, ref) => <LastPage { ...props } ref={ ref }/>),
+ NextPage: forwardRef((props, ref) => <ChevronRight { ...props } ref={ ref }/>),
+ PreviousPage: forwardRef((props, ref) => <ChevronLeft { ...props } ref={ ref }/>),
+ ResetSearch: forwardRef((props, ref) => <Clear { ...props } ref={ ref }/>),
+ Search: forwardRef((props, ref) => <Search { ...props } ref={ ref }/>),
+ SortArrow: forwardRef((props, ref) => <ArrowDownward { ...props } ref={ ref }/>),
+ ThirdStateCheck: forwardRef((props, ref) => <Remove { ...props } ref={ ref }/>),
+ ViewColumn: forwardRef((props, ref) => <ViewColumn { ...props } ref={ ref }/>)
+ }
+ };
+
+ constructor(props, context) {
+ super(props, context);
+ this.handleClose = this.handleClose.bind(this);
+ this.renderPdpGroupDropBox = this.renderPdpGroupDropBox.bind(this);
+ this.handlePdpGroupChange = this.handlePdpGroupChange.bind(this);
+ this.handlePrefixGrouping = this.handlePrefixGrouping.bind(this);
+ this.handleDeletePolicy = this.handleDeletePolicy.bind(this);
+ this.disableAlert = this.disableAlert.bind(this);
+ this.getAllPolicies = this.getAllPolicies.bind(this);
+ this.getAllToscaModels = this.getAllToscaModels.bind(this);
+ this.getAllPolicies();
+ this.getAllToscaModels();
+ }
+
+ getAllToscaModels() {
+ PolicyToscaService.getToscaPolicyModels().then(toscaModelsList => {
+ this.setState({ toscaModelsListData: toscaModelsList });
+ });
+ }
+
+ handlePdpGroupChange(e) {
+ let pdpSplit = e.value.split("/");
+ let selectedPdpGroup = pdpSplit[0];
+ let selectedSubPdpGroup = pdpSplit[1];
+ if (typeof selectedSubPdpGroup !== "undefined") {
+ let temp = this.state.policiesListData;
+ temp[this.state.selectedRowId]["pdpGroupInfo"] = { "pdpGroup": selectedPdpGroup, "pdpSubGroup": selectedSubPdpGroup };
+ this.setState({ policiesListData: temp });
+ } else {
+ delete this.state.policiesListData[this.state.selectedRowId]["pdpGroupInfo"];
+ }
+ }
+
+ renderPdpGroupDropBox(dataRow) {
+ let optionItems = [{ label: "NOT DEPLOYED", value: "NOT DEPLOYED" }];
+ let selectedItem = { label: "NOT DEPLOYED", value: "NOT DEPLOYED" };
+ if (typeof dataRow.supportedPdpGroups !== "undefined") {
+ for (const pdpGroup of dataRow["supportedPdpGroups"]) {
+ for (const pdpSubGroup of Object.values(pdpGroup)[0]) {
+ optionItems.push({
+ label: Object.keys(pdpGroup)[0] + "/" + pdpSubGroup,
+ value: Object.keys(pdpGroup)[0] + "/" + pdpSubGroup
+ });
+ }
+ }
+ }
+ if (typeof dataRow.pdpGroupInfo !== "undefined") {
+ selectedItem = {
+ label: dataRow["pdpGroupInfo"]["pdpGroup"] + "/" + dataRow["pdpGroupInfo"]["pdpSubGroup"],
+ value: dataRow["pdpGroupInfo"]["pdpGroup"] + "/" + dataRow["pdpGroupInfo"]["pdpSubGroup"]
+ };
+ }
+ return (<div style={ { width: '250px' } }><Select value={ selectedItem } options={ optionItems } onChange={ this.handlePdpGroupChange }/></div>);
+ }
+
+ getAllPolicies() {
+ PolicyService.getPoliciesList().then(allPolicies => {
+ this.setState({ policiesListData: allPolicies["policies"] })
+ });
+ }
+
+ handleClose() {
+ this.setState({ show: false });
+ this.props.history.push('/')
+ }
+
+ handlePrefixGrouping(event) {
+ this.setState({ prefixGrouping: event.target.checked });
+ }
+
+ handleDeletePolicy(event, rowData) {
+ PolicyService.deletePolicy(rowData["type"], rowData["type_version"], rowData["name"], rowData["version"]).then(
+ respPolicyDeletion => {
+ if (typeof (respPolicyDeletion) === "undefined") {
+ //it indicates a failure
+ this.setState({
+ showFailAlert: true,
+ showMessage: 'Policy Deletion Failure'
+ });
+ } else {
+ this.setState({
+ showSuccessAlert: true,
+ showMessage: 'Policy successfully Deleted'
+ });
+ }
+ this.getAllPolicies();
+ }
+ )
+ }
+
+ disableAlert() {
+ this.setState({ showSuccessAlert: false, showFailAlert: false });
+ }
+
+ renderPoliciesTab() {
+ return (
+ <Tab eventKey="policies" title="Policies in Policy Framework">
+ <Modal.Body>
+ <FormControlLabel
+ control={ <Switch checked={ this.state.prefixGrouping } onChange={ this.handlePrefixGrouping }/> }
+ label="Group by prefix"
+ />
+ <MaterialTable
+ title={ "Policies" }
+ data={ this.state.policiesListData }
+ columns={ this.state.policyColumnsDefinition }
+ icons={ this.state.tableIcons }
+ 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: ArrowForwardIosIcon,
+ tooltip: 'Show Configuration',
+ render: rowData => {
+ return (
+ <DetailedRow>
+ <PolicyEditor policyModelType={ rowData["type"] } policyModelTypeVersion={ rowData["type_version"] } policyName={ rowData["name"] } policyVersion={ rowData["version"] }
+ policyProperties={ rowData["properties"] } policyUpdateFunction={ this.getAllPolicies }/>
+ </DetailedRow>
+ )
+ },
+ },
+ {
+ icon: DehazeIcon,
+ tooltip: 'Show Raw Data',
+ render: rowData => {
+ return (
+ <DetailedRow>
+ <pre>{ JSON.stringify(rowData, null, 2) }</pre>
+ </DetailedRow>
+ )
+ },
+ },
+ ] }
+ actions={ [
+ {
+ icon: forwardRef((props, ref) => <DeleteRoundedIcon { ...props } ref={ ref }/>),
+ tooltip: 'Delete Policy',
+ onClick: (event, rowData) => this.handleDeletePolicy(event, rowData)
+ }
+ ] }
+ />
+ </Modal.Body>
+ </Tab>
+ );
+ }
+
+ renderToscaTab() {
+ return (
+ <Tab eventKey="tosca models" title="Tosca Models in Policy Framework">
+ <Modal.Body>
+ <FormControlLabel
+ control={ <Switch checked={ this.state.prefixGrouping } onChange={ this.handlePrefixGrouping }/> }
+ label="Group by prefix"
+ />
+ <MaterialTable
+ title={ "Tosca Models" }
+ data={ this.state.toscaModelsListData }
+ columns={ this.state.toscaColumnsDefinition }
+ icons={ this.state.tableIcons }
+ 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: ArrowForwardIosIcon,
+ tooltip: 'Show Tosca',
+ render: rowData => {
+ return (
+ <DetailedRow>
+ <ToscaViewer toscaData={ rowData }/>
+ </DetailedRow>
+ )
+ },
+ },
+ {
+ icon: DehazeIcon,
+ tooltip: 'Show Raw Data',
+ render: rowData => {
+ return (
+ <DetailedRow>
+ <pre>{ JSON.stringify(rowData, null, 2) }</pre>
+ </DetailedRow>
+ )
+ },
+ },
+ {
+ icon: AddIcon,
+ tooltip: 'Create a policy from this model',
+ render: rowData => {
+ return (
+ <DetailedRow>
+ <PolicyEditor policyModelType={ rowData["policyModelType"] } policyModelTypeVersion={ rowData["version"] } policyProperties={ {} } policyUpdateFunction={ this.getAllPolicies }/>
+ </DetailedRow>
+ )
+ },
+ },
+ ] }
+ />
+ </Modal.Body>
+ </Tab>
+ );
+ }
+
+ render() {
+ return (
+ <ModalStyled size="xl" show={ this.state.show } onHide={ this.handleClose } backdrop="static" keyboard={ false }>
+ <Modal.Header closeButton>
+ </Modal.Header>
+ <Tabs id="controlled-tab-example" activeKey={ this.state.key } onSelect={ key => this.setState({ key, selectedRowData: {} }) }>
+ { this.renderPoliciesTab() }
+ { this.renderToscaTab() }
+ </Tabs>
+ <Alert variant="success" show={ this.state.showSuccessAlert } onClose={ this.disableAlert } dismissible>
+ <DivWhiteSpaceStyled>
+ { this.state.showMessage }
+ </DivWhiteSpaceStyled>
+ </Alert>
+ <Alert variant="danger" show={ this.state.showFailAlert } onClose={ this.disableAlert } dismissible>
+ <DivWhiteSpaceStyled>
+ { this.state.showMessage }
+ </DivWhiteSpaceStyled>
+ </Alert>
+ <Modal.Footer>
+ <Button variant="secondary" onClick={ this.handleClose }>Close</Button>
+ </Modal.Footer>
+ </ModalStyled>
+ );
+ }
+}