From a090a83268cfd8b1341c266d32ccfb670b9f0e8c Mon Sep 17 00:00:00 2001 From: brunomilitzer Date: Mon, 30 Aug 2021 18:46:53 +0100 Subject: Add Delete Instantiation Functionality. Update UI to centralize Instantiation Management Created Delete button to delete the instantiations Fixed Change Order State Functionality Issue-ID: POLICY-3558 Change-Id: I2efb00ce041ab4fc217e06ed72385ad8ea1b10fb Signed-off-by: brunomilitzer --- gui-clamp/ui-react/src/LoopUI.js | 8 +- .../ui-react/src/__snapshots__/LoopUI.test.js.snap | 6 +- .../src/__snapshots__/OnapClamp.test.js.snap | 6 +- gui-clamp/ui-react/src/api/ControlLoopService.js | 27 +- .../dialogs/ControlLoop/ChangeOrderStateModal.js | 176 ++++++++++++ .../dialogs/ControlLoop/InstanceModal.js | 290 ------------------- .../dialogs/ControlLoop/InstanceModal.test.js | 70 ----- .../dialogs/ControlLoop/InstancePropertiesModal.js | 307 +++++++++++++++++++++ .../ControlLoop/InstancePropertiesModal.test.js | 69 +++++ .../ControlLoop/InstantiationManagementModal.js | 222 ++++++++++----- .../__snapshots__/InstanceModal.test.js.snap | 68 ----- .../InstancePropertiesModal.test.js.snap | 68 +++++ .../dialogs/InstantiationManagementModal.test.js | 14 +- gui-clamp/ui-react/src/components/menu/MenuBar.js | 4 +- .../menu/__snapshots__/MenuBar.test.js.snap | 134 +-------- 15 files changed, 815 insertions(+), 654 deletions(-) create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/ChangeOrderStateModal.js delete mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstanceModal.js delete mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstanceModal.test.js create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstancePropertiesModal.js create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstancePropertiesModal.test.js delete mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/InstanceModal.test.js.snap create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/InstancePropertiesModal.test.js.snap (limited to 'gui-clamp/ui-react/src') diff --git a/gui-clamp/ui-react/src/LoopUI.js b/gui-clamp/ui-react/src/LoopUI.js index 79943bf..e593a08 100644 --- a/gui-clamp/ui-react/src/LoopUI.js +++ b/gui-clamp/ui-react/src/LoopUI.js @@ -55,7 +55,8 @@ import ReadAndConvertYaml from "./components/dialogs/ControlLoop/ReadAndConvertY import MonitorInstantiation from "./components/dialogs/ControlLoop/MonitorInstantiation"; import GetLocalToscaFileForUpload from "./components/dialogs/ControlLoop/GetLocalToscaFileForUpload"; import CommissioningModal from "./components/dialogs/ControlLoop/CommissioningModal"; -import InstanceModal from "./components/dialogs/ControlLoop/InstanceModal"; +import InstancePropertiesModal from "./components/dialogs/ControlLoop/InstancePropertiesModal"; +import ChangeOrderStateModal from "./components/dialogs/ControlLoop/ChangeOrderStateModal"; import InstantiationManagementModal from "./components/dialogs/ControlLoop/InstantiationManagementModal"; const StyledMainDiv = styled.div` @@ -387,8 +388,9 @@ export default class LoopUI extends React.Component { showFailAlert={ this.showFailAlert }/>) } /> () }/> - () }/> - () }/> + () }/> + () }/> + () }/> ); } diff --git a/gui-clamp/ui-react/src/__snapshots__/LoopUI.test.js.snap b/gui-clamp/ui-react/src/__snapshots__/LoopUI.test.js.snap index abfcb8d..222b2a7 100644 --- a/gui-clamp/ui-react/src/__snapshots__/LoopUI.test.js.snap +++ b/gui-clamp/ui-react/src/__snapshots__/LoopUI.test.js.snap @@ -90,13 +90,17 @@ exports[`Verify LoopUI Test the render method 1`] = ` render={[Function]} /> +
+
{ + const [show, setShow] = useState(true); + const [windowLocationPathnameGet, setWindowLocationPathnameGet] = useState(''); + const [windowLocationPathNameSave, setWindowLocationPathNameSave] = useState(''); + const [controlLoopIdentifierList, setControlLoopIdentifierList] = useState([]); + const [orderedState, setOrderedState] = useState(''); + const [toscaOrderStateObject, setToscaOrderStateObject] = useState({}); + const [instantiationOrderStateError, setInstantiationOrderStateError] = useState(false); + const [instantiationOrderStateMsgError, setInstantiationOrderStateMsgError] = useState({}); + const [alertMessage, setAlertMessage] = useState(null); + + useEffect(async () => { + setWindowLocationPathnameGet(window.location.pathname); + + const instantiationOrderState = await ControlLoopService.getInstanceOrderState( + props.location.instantiationName, + props.location.instantiationVersion, windowLocationPathnameGet) + .catch(error => error.message); + + const orderStateJson = await instantiationOrderState.json(); + + console.log(orderStateJson); + + if (!instantiationOrderState.ok || orderStateJson['controlLoopIdentifierList'].length === 0) { + setInstantiationOrderStateError(true); + setInstantiationOrderStateMsgError(orderStateJson); + } else { + setControlLoopIdentifierList(orderStateJson['controlLoopIdentifierList']); + setOrderedState(orderStateJson['orderedState']); + } + }, []); + + const handleDropSelect = (event) => { + console.log("handleDropDownChange called"); + + const stateChangeObject = { + orderedState: event, + controlLoopIdentifierList: controlLoopIdentifierList + } + setToscaOrderStateObject(stateChangeObject); + setOrderedState(event); + } + + const handleSave = async () => { + console.log("handleSave called"); + setWindowLocationPathNameSave(window.location.pathname); + + const response = await ControlLoopService.changeInstanceOrderState( + toscaOrderStateObject, + windowLocationPathNameSave).catch(error => error.message); + + if (response.ok) { + successAlert(); + } else { + await errorAlert(response); + } + } + + const handleClose = () => { + console.log('handleClose called'); + setShow(false); + props.history.push('/'); + } + + const successAlert = () => { + console.log("successAlert called"); + setAlertMessage( + Order State Changed Success +

Order State Changed was successfully changed

+
+
); + } + + const errorAlert = async (response) => { + console.log("errorAlert called"); + setAlertMessage( + Order State Changed Failure +

An error occurred while trying to change order state

+

Status code: { await response.status } : { response.statusText }

+

Status Text: { await response.text() }

+
+
); + } + + return ( + + + Manage Instantiation + +
+ + + + + Select Order State + + + UNINITIALISED + PASSIVE + RUNNING + + + { + controlLoopIdentifierList.map((clIdList, index) => ( + + )) + } + + Can't get instantiation ordered state:
{ JSON.stringify(instantiationOrderStateMsgError, null, 2) }
+
+ + { alertMessage } + +
+ + + + +
+ ); +} + +export default ChangeOrderStateModal; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstanceModal.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstanceModal.js deleted file mode 100644 index 0380fa6..0000000 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstanceModal.js +++ /dev/null @@ -1,290 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation. - * ================================================================================ - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -import React, { useEffect, useState } from "react"; -import Modal from "react-bootstrap/Modal"; -import styled from "styled-components"; -import Button from "react-bootstrap/Button"; -import ControlLoopService from "../../../api/ControlLoopService"; -import { JSONEditor } from "@json-editor/json-editor"; -import Alert from "react-bootstrap/Alert"; - -const ModalStyled = styled(Modal)` - @media (min-width: 800px) { - .modal-xl { - max-width: 96%; - } - } - background-color: transparent; -` - -const DivWhiteSpaceStyled = styled.div` - overflow: auto; - min-width: 100%; - max-height: 300px; - padding: 5px 5px 0px 5px; - text-align: center; -` - -const AlertStyled = styled(Alert)` - margin-top: 10px; -` - -const templateName = "ToscaServiceTemplateSimple"; -const templateVersion = "1.0.0"; -let tempJsonEditor = null; - -const InstanceModal = (props) => { - const [show, setShow] = useState(true); - const [windowLocationPathname, setWindowLocationPathname] = useState(''); - const [toscaFullTemplate, setToscaFullTemplate] = useState({}); - const [toscaFilteredInitialValues, setToscaFilteredInitialValues] = useState({}); - const [toscaJsonSchema, setToscaJsonSchema] = useState({}); - const [jsonEditor, setJsonEditor] = useState(null); - const [alertMessage, setAlertMessage] = useState(null); - const [instancePropertiesGlobal, setInstancePropertiesGlobal] = useState({}); - const [serviceTemplateResponseOk, setServiceTemplateResponseOk] = useState(true); - const [instancePropertiesResponseOk, setInstancePropertiesResponseOk] = useState(true); - - useEffect(async () => { - const toscaInstanceProperties = await ControlLoopService.getCommonOrInstanceProperties(templateName, templateVersion, windowLocationPathname, false).catch(error => error.message); - - const toscaTemplateResponse = await ControlLoopService.getToscaTemplate(templateName, templateVersion, windowLocationPathname).catch(error => error.message); - - if (!toscaInstanceProperties.ok) { - const errorResponse = await toscaInstanceProperties.json() - console.log(errorResponse) - setInstancePropertiesGlobal(errorResponse); - setInstancePropertiesResponseOk(false); - } - - if (!toscaTemplateResponse.ok) { - const errorResponse = await toscaTemplateResponse.json() - console.log(errorResponse) - setToscaFullTemplate(errorResponse) - setServiceTemplateResponseOk(false); - } - - if (toscaTemplateResponse.ok && toscaInstanceProperties.ok) { - await parseJsonSchema(toscaTemplateResponse, toscaInstanceProperties); - } - - }, []); - - const parseJsonSchema = async (fullTemplate, initialProperties) => { - - const fullJsonSchemaTemplate = await fullTemplate.json(); - setToscaFullTemplate(fullJsonSchemaTemplate); - - const filteredInitialStartValues = {}; - - const instanceProperties = await initialProperties.json().then(properties => { - const filteredTemplateObj = {}; - const propertiesTemplateArray = Object.entries(properties); - - propertiesTemplateArray.forEach(([key, value]) => { - const propertiesObj = { - properties: value.properties - } - - const propValues = {}; - filteredTemplateObj[key] = propertiesObj; - - const jsonNodeSchemaKey = fullJsonSchemaTemplate.topology_template.node_templates[key] - - Object.entries(propertiesObj.properties).forEach(([pKey, pValue]) => { - propValues[pKey] = jsonNodeSchemaKey.properties[pKey]; - }); - - filteredInitialStartValues[key] = propValues; - }); - - setToscaFilteredInitialValues(filteredInitialStartValues); - - return filteredTemplateObj; - }); - - const propertySchema = makeSchemaForInstanceProperties(instanceProperties); - setToscaJsonSchema(propertySchema); - - tempJsonEditor = createJsonEditor(propertySchema, filteredInitialStartValues); - setJsonEditor(tempJsonEditor); - } - - const makeSchemaForInstanceProperties = (instanceProps) => { - const instancePropsArray = Object.entries(instanceProps); - - const newSchemaObject = {}; - - newSchemaObject.title = "InstanceProperties"; - newSchemaObject.type = "object"; - newSchemaObject.properties = {}; - - instancePropsArray.forEach(([key, value]) => { - - const propertiesObject = {}; - - Object.entries(value.properties).forEach(([pKey, pValue]) => { - propertiesObject[pKey] = { - type: getType(pValue.type) - } - }); - - newSchemaObject.properties[key] = { - options: { - "collapsed": true - }, - properties: propertiesObject - } - }); - - return newSchemaObject; - } - - const getType = (pType) => { - switch (pType) { - case "string": - return "string"; - case "integer": - return "integer"; - case "list": - return "array"; - case "object": - return "object"; - default: - return "object"; - - } - } - - const createJsonEditor = (fullSchema, instanceProperties) => { - JSONEditor.defaults.options.collapse = true; - - return new JSONEditor(document.getElementById("editor"), - { - schema: fullSchema, - startval: instanceProperties, - theme: 'bootstrap4', - iconlib: 'fontawesome5', - object_layout: 'normal', - 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, - }); - } - - const handleClose = () => { - console.log('handleClose called'); - setShow(false); - props.history.push('/'); - } - - const updateTemplate = (jsonEditorValues) => { - const nodeTemplates = toscaFullTemplate.topology_template.node_templates; - const instanceDataProperties = Object.entries(jsonEditorValues); - - instanceDataProperties.forEach(([key, value]) => { - const nodeTemplatesKey = nodeTemplates[key] - Object.entries(value).forEach(([pKey, pValue]) => { - nodeTemplatesKey.properties[pKey] = pValue - }); - }); - - toscaFullTemplate.topology_template.node_templates = nodeTemplates; - - setToscaFullTemplate(toscaFullTemplate); - - } - - const handleSave = async () => { - console.log("handleSave called") - - setWindowLocationPathname(window.location.pathname); - - updateTemplate(jsonEditor.getValue()); - - const response = await ControlLoopService.createInstanceProperties(toscaFullTemplate, windowLocationPathname).catch(error => error.message); - - if (response.ok) { - successAlert(); - } else { - await errorAlert(response); - } - } - - const successAlert = () => { - console.log("successAlert called"); - setAlertMessage( - Instantiation Properties Success -

Instance Properties was successfully saved

-
-
); - } - - const errorAlert = async (response) => { - console.log("errorAlert called"); - setAlertMessage( - Instantiation Properties Failure -

An error occurred while trying to save

-

Status code: { await response.status } : { response.statusText }

-

Status Text: { await response.text() }

-
-
); - } - - return ( - - - Change Tosca Instance Properties - -
- -
- Can't get service template:
{ JSON.stringify(toscaFullTemplate, null, 2) }
- Can't get instance properties:
{ JSON.stringify(instancePropertiesGlobal, null, 2) }
- - - { alertMessage } - -
- - - - - - ); -} - -export default InstanceModal; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstanceModal.test.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstanceModal.test.js deleted file mode 100644 index 36a77d7..0000000 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstanceModal.test.js +++ /dev/null @@ -1,70 +0,0 @@ -/* - * ============LICENSE_START======================================================= - * Copyright (C) 2021 Nordix Foundation. - * ================================================================================ - * 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. - * - * SPDX-License-Identifier: Apache-2.0 - * ============LICENSE_END========================================================= - */ - -import { mount, shallow } from "enzyme"; -import React from "react"; -import InstanceModal from "./InstanceModal"; -import toJson from "enzyme-to-json"; -import { createMemoryHistory } from "history"; -import MonitorInstantiation from "./MonitorInstantiation"; -import { act } from "react-dom/test-utils"; - -describe('Verify MonitoringInstantiation', () => { - - it("renders without crashing", () => { - shallow(); - }); - - it("renders correctly", () => { - const tree = shallow(); - expect(toJson(tree)).toMatchSnapshot(); - }); - - it('should have submit button element', () => { - const container = shallow() - expect(container.find('[variant="primary"]').length).toEqual(1); - }); - - it('should have close button element', () => { - const container = shallow() - expect(container.find('[variant="secondary"]').length).toEqual(1); - }); - - it('handleCreateUpdateToscaInstanceProperties called when submit button clicked', () => { - const history = createMemoryHistory(); - const component = mount() - const logSpy = jest.spyOn(console, 'log'); - - act(() => { - component.find('[variant="primary"]').simulate('click'); - expect(logSpy).toHaveBeenCalledWith('handleSave called'); - }); - }); - - it('handleClose called when close button clicked', () => { - const history = createMemoryHistory(); - const component = mount() - const logSpy = jest.spyOn(console, 'log'); - - act(() => { - component.find('[variant="secondary"]').simulate('click'); - expect(logSpy).toHaveBeenCalledWith('handleClose called'); - }); - }); -}); diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstancePropertiesModal.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstancePropertiesModal.js new file mode 100644 index 0000000..8343237 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstancePropertiesModal.js @@ -0,0 +1,307 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +import React, { useEffect, useState } from "react"; +import Modal from "react-bootstrap/Modal"; +import styled from "styled-components"; +import Button from "react-bootstrap/Button"; +import ControlLoopService from "../../../api/ControlLoopService"; +import { JSONEditor } from "@json-editor/json-editor"; +import Alert from "react-bootstrap/Alert"; +import * as PropTypes from "prop-types"; +import Form from "react-bootstrap/Form"; +import Spinner from "react-bootstrap/Spinner"; + +const ModalStyled = styled(Modal)` + @media (min-width: 800px) { + .modal-xl { + max-width: 96%; + } + } + background-color: transparent; +` + +const DivWhiteSpaceStyled = styled.div` + overflow: auto; + min-width: 100%; + max-height: 300px; + padding: 5px 5px 0px 5px; + text-align: center; +` + +const AlertStyled = styled(Alert)` + margin-top: 10px; +` + +const templateName = "ToscaServiceTemplateSimple"; +const templateVersion = "1.0.0"; +let tempJsonEditor = null; + +function Fragment(props) { + return null; +} + +Fragment.propTypes = { children: PropTypes.node }; +const InstancePropertiesModal = (props) => { + const [show, setShow] = useState(true); + const [windowLocationPathname, setWindowLocationPathname] = useState(''); + const [toscaFullTemplate, setToscaFullTemplate] = useState({}); + const [jsonEditor, setJsonEditor] = useState(null); + const [alertMessage, setAlertMessage] = useState(null); + const [instancePropertiesGlobal, setInstancePropertiesGlobal] = useState({}); + const [serviceTemplateResponseOk, setServiceTemplateResponseOk] = useState(true); + const [instancePropertiesResponseOk, setInstancePropertiesResponseOk] = useState(true); + const [instanceName, setInstanceName] = useState('') + const [isLoading, setIsLoading] = useState(true); + + useEffect(async () => { + const toscaInstanceProperties = await ControlLoopService.getCommonOrInstanceProperties(templateName, templateVersion, windowLocationPathname, false).catch(error => error.message); + + const toscaTemplateResponse = await ControlLoopService.getToscaTemplate(templateName, templateVersion, windowLocationPathname).catch(error => error.message); + + if (!toscaInstanceProperties.ok) { + const errorResponse = await toscaInstanceProperties.json() + console.log(errorResponse) + setInstancePropertiesGlobal(errorResponse); + setInstancePropertiesResponseOk(false); + } + + if (!toscaTemplateResponse.ok) { + const errorResponse = await toscaTemplateResponse.json() + console.log(errorResponse) + setToscaFullTemplate(errorResponse) + setServiceTemplateResponseOk(false); + } + + if (toscaTemplateResponse.ok && toscaInstanceProperties.ok) { + await parseJsonSchema(toscaTemplateResponse, toscaInstanceProperties); + } + + }, []); + + const parseJsonSchema = async (fullTemplate, initialProperties) => { + + const fullJsonSchemaTemplate = await fullTemplate.json(); + setToscaFullTemplate(fullJsonSchemaTemplate); + + console.log(fullJsonSchemaTemplate); + + const filteredInitialStartValues = {}; + + const instanceProperties = await initialProperties.json().then(properties => { + const filteredTemplateObj = {}; + const propertiesTemplateArray = Object.entries(properties); + + propertiesTemplateArray.forEach(([key, value]) => { + const propertiesObj = { + properties: value.properties + } + + const propValues = {}; + filteredTemplateObj[key] = propertiesObj; + + const jsonNodeSchemaKey = fullJsonSchemaTemplate.topology_template.node_templates[key] + + Object.entries(propertiesObj.properties).forEach(([pKey, pValue]) => { + propValues[pKey] = jsonNodeSchemaKey.properties[pKey]; + }); + + filteredInitialStartValues[key] = propValues; + }); + + return filteredTemplateObj; + }); + + const propertySchema = makeSchemaForInstanceProperties(instanceProperties); + + tempJsonEditor = createJsonEditor(propertySchema, filteredInitialStartValues); + setJsonEditor(tempJsonEditor); + } + + const makeSchemaForInstanceProperties = (instanceProps) => { + const instancePropsArray = Object.entries(instanceProps); + + const newSchemaObject = {}; + + newSchemaObject.title = "InstanceProperties"; + newSchemaObject.type = "object"; + newSchemaObject.properties = {}; + + instancePropsArray.forEach(([key, value]) => { + + const propertiesObject = {}; + + Object.entries(value.properties).forEach(([pKey, pValue]) => { + propertiesObject[pKey] = { + type: getType(pValue.type) + } + }); + + newSchemaObject.properties[key] = { + options: { + "collapsed": true + }, + properties: propertiesObject + } + }); + + return newSchemaObject; + } + + const getType = (pType) => { + switch (pType) { + case "map": + return "string"; + case "string": + return "string"; + case "integer": + return "integer"; + case "list": + return "array"; + case "object": + return "object"; + default: + return "object"; + + } + } + + const createJsonEditor = (fullSchema, instanceProperties) => { + console.log(props.location.instanceName) + setIsLoading(false) + JSONEditor.defaults.options.collapse = true; + + return new JSONEditor(document.getElementById("editor"), + { + schema: fullSchema, + startval: instanceProperties, + theme: 'bootstrap4', + iconlib: 'fontawesome5', + object_layout: 'normal', + 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, + }); + } + + const handleClose = () => { + console.log('handleClose called'); + setShow(false); + props.history.push('/'); + } + + const updateTemplate = (jsonEditorValues) => { + const nodeTemplates = toscaFullTemplate.topology_template.node_templates; + const instanceDataProperties = Object.entries(jsonEditorValues); + + instanceDataProperties.forEach(([key, value]) => { + const nodeTemplatesKey = nodeTemplates[key] + Object.entries(value).forEach(([pKey, pValue]) => { + nodeTemplatesKey.properties[pKey] = pValue + }); + }); + + toscaFullTemplate.topology_template.node_templates = nodeTemplates; + + setToscaFullTemplate(toscaFullTemplate); + + } + + const handleSave = async () => { + console.log("handleSave called") + + console.log("instanceName to be saved is: " + instanceName) + + setWindowLocationPathname(window.location.pathname); + + updateTemplate(jsonEditor.getValue()); + + const response = await ControlLoopService.createInstanceProperties(instanceName, toscaFullTemplate, windowLocationPathname).catch(error => error.message); + + if (response.ok) { + successAlert(); + } else { + await errorAlert(response); + } + } + + const handleNameChange = (e) => { + setInstanceName(e.target.value) + } + + const successAlert = () => { + console.log("successAlert called"); + setAlertMessage( + Instantiation Properties Success +

Instance Properties was successfully saved

+
+
); + } + + const errorAlert = async (response) => { + console.log("errorAlert called"); + setAlertMessage( + Instantiation Properties Failure +

An error occurred while trying to save

+

Status code: { await response.status } : { response.statusText }

+

Status Text: { await response.text() }

+
+
); + } + + return ( + + + Create Tosca Instance Properties + +
+ +
+ Can't get service template:
{ JSON.stringify(toscaFullTemplate, null, 2) }
+ Can't get instance properties:
{ JSON.stringify(instancePropertiesGlobal, null, 2) }
+ + + { alertMessage } + +
+ + + + + + ); +} + +export default InstancePropertiesModal; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstancePropertiesModal.test.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstancePropertiesModal.test.js new file mode 100644 index 0000000..141999c --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstancePropertiesModal.test.js @@ -0,0 +1,69 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation. + * ================================================================================ + * 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. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +import { mount, shallow } from "enzyme"; +import React from "react"; +import InstancePropertiesModal from "./InstancePropertiesModal"; +import toJson from "enzyme-to-json"; +import { createMemoryHistory } from "history"; +import { act } from "react-dom/test-utils"; + +describe('Verify MonitoringInstantiation', () => { + + it("renders without crashing", () => { + shallow(); + }); + + it("renders correctly", () => { + const tree = shallow(); + expect(toJson(tree)).toMatchSnapshot(); + }); + + it('should have submit button element', () => { + const container = shallow() + expect(container.find('[variant="primary"]').length).toEqual(1); + }); + + it('should have close button element', () => { + const container = shallow() + expect(container.find('[variant="secondary"]').length).toEqual(1); + }); + + it('handleCreateUpdateToscaInstanceProperties called when submit button clicked', () => { + const history = createMemoryHistory(); + const component = mount() + const logSpy = jest.spyOn(console, 'log'); + + act(() => { + component.find('[variant="primary"]').simulate('click'); + expect(logSpy).toHaveBeenCalledWith('handleSave called'); + }); + }); + + it('handleClose called when close button clicked', () => { + const history = createMemoryHistory(); + const component = mount() + const logSpy = jest.spyOn(console, 'log'); + + act(() => { + component.find('[variant="secondary"]').simulate('click'); + expect(logSpy).toHaveBeenCalledWith('handleClose called'); + }); + }); +}); diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationManagementModal.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationManagementModal.js index c6c1eb9..a0a849c 100644 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationManagementModal.js +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationManagementModal.js @@ -15,25 +15,28 @@ * * SPDX-License-Identifier: Apache-2.0 * ============LICENSE_END========================================================= + * + * */ -import styled from "styled-components"; import Modal from "react-bootstrap/Modal"; +import { Alert, Container, Dropdown, Table } from "react-bootstrap"; import Button from "react-bootstrap/Button"; -import React, { useContext, useEffect, useRef, useState } from "react"; -import InstantiationOrderStateChangeItem from "./InstantiationOrderStateChangeItem"; +import React, { useEffect, useState } from "react"; +import styled from "styled-components"; +import { Link } from "react-router-dom"; import ControlLoopService from "../../../api/ControlLoopService"; -import { Alert, Container, Dropdown } from "react-bootstrap"; +import Row from "react-bootstrap/Row"; const ModalStyled = styled(Modal)` - @media (min-width: 800px) { - .modal-xl { - max-width: 96%; - } - } background-color: transparent; ` +const HorizontalSpace = styled.div` + padding-right: 2px; + padding-left: 2px; +`; + const DivWhiteSpaceStyled = styled.div` overflow: auto; min-width: 100%; @@ -41,55 +44,55 @@ const DivWhiteSpaceStyled = styled.div` padding: 5px 5px 0px 5px; text-align: center; ` - -const AlertStyled = styled(Alert)` - margin-top: 10px; -` - const InstantiationManagementModal = (props) => { const [show, setShow] = useState(true); - const [windowLocationPathnameGet, setWindowLocationPathnameGet] = useState(''); - const [windowLocationPathNameSave, setWindowLocationPathNameSave] = useState(''); - const [controlLoopIdentifierList, setControlLoopIdentifierList] = useState([]); - const [orderedState, setOrderedState] = useState(''); - const [toscaOrderStateObject, setToscaOrderStateObject] = useState({}); - const [instantiationOrderStateOk, setInstantiationOrderStateOk] = useState(true); - const [instantiationOrderStateError, setInstantiationOrderStateError] = useState({}); + const [windowLocationPathName, setWindowLocationPathName] = useState(''); + const [windowLocationPathNameDelete, setWindowLocationPathNameDelete] = useState(''); + + const [instantiationList, setInstantiationList] = useState([]); const [alertMessage, setAlertMessage] = useState(null); useEffect(async () => { - setWindowLocationPathnameGet(window.location.pathname); + setWindowLocationPathName(window.location.pathname); - const instantiationOrderState = await ControlLoopService.getInstanceOrderState(windowLocationPathnameGet) - .catch(error => error.message); + const response = await ControlLoopService.getControlLoopInstantiation(windowLocationPathName); - const orderStateJson = await instantiationOrderState.json(); + const instantiationListJson = await response.json(); - if (!instantiationOrderState.ok || orderStateJson['controlLoopIdentifierList'].length === 0) { - setInstantiationOrderStateOk(true); - setInstantiationOrderStateError(orderStateJson); - } else { - setControlLoopIdentifierList(orderStateJson['controlLoopIdentifierList']); - setOrderedState(orderStateJson['orderedState']); - } - }, []); + const parsedInstantiationList = instantiationListJson['controlLoopList'].map((instance, index) => { + return { + index: index, + name: instance['name'], + version: instance['version'], + orderedState: instance['orderedState'], + currentState: instance['state'], + disableDelete: instance['state'] !== 'UNINITIALISED' + } + }); - const handleDropSelect = (event) => { - console.log("handleDropDownChange called"); + setInstantiationList(parsedInstantiationList); + }, []); - const stateChangeObject = { - orderedState: event, - controlLoopIdentifierList: controlLoopIdentifierList + const getBackgroundColor = (index) => { + if (index % 2 === 0) { + return 'Silver'; } - setToscaOrderStateObject(stateChangeObject); - orderStateContext.orderState = stateChangeObject; + + return 'White'; } - const handleSave = async () => { - console.log("handleSave called"); - setWindowLocationPathNameSave(window.location.pathname); + const deleteInstantiationHandler = async (instantiation, index) => { + console.log("deleteInstantiationHandler called"); + setWindowLocationPathNameDelete(window.location.pathname); + + const name = instantiation.name; + const version = instantiation.version; + + console.log(window.location.pathname); + + const response = await ControlLoopService.deleteInstantiation(name, version, windowLocationPathNameDelete); - const response = await ControlLoopService.changeInstanceOrderState(toscaOrderStateObject, windowLocationPathNameSave).catch(error => error.message); + updateList(index); if (response.ok) { successAlert(); @@ -98,8 +101,34 @@ const InstantiationManagementModal = (props) => { } } + const updateList = (index) => { + console.log("updateList called") + console.log(instantiationList) + + const updatedList = [...instantiationList]; + updatedList.splice(index, 1); + + setInstantiationList(updatedList); + } + + const renderDeleteButton = (instantiation, index) => { + if (instantiation.disableDelete) { + return ( + + ); + + } else { + return ( + + ); + } + } + const handleClose = () => { - console.log('handleClose called'); + console.log("handleClose called"); setShow(false); props.history.push('/'); } @@ -107,8 +136,8 @@ const InstantiationManagementModal = (props) => { const successAlert = () => { console.log("successAlert called"); setAlertMessage( - Order State Changed Success -

Order State Changed was successfully changed

+ Deletion of Instantiation Success +

Deletion of Instantiation was successful!


); } @@ -116,52 +145,93 @@ const InstantiationManagementModal = (props) => { const errorAlert = async (response) => { console.log("errorAlert called"); setAlertMessage( - Order State Changed Failure -

An error occurred while trying to change order state

+ Deletion of Instantiation Failure +

An error occurred while trying to delete instantiation

Status code: { await response.status } : { response.statusText }

Status Text: { await response.text() }


); } + const clearErrors = () => { + console.log("clearErrors called"); + setAlertMessage(null); + } + return ( - - Manage Instantiation + Manage Instances -
- - - - - Select Order State - - - UNINITIALISED - PASSIVE - RUNNING - - - { - controlLoopIdentifierList.map((clIdList, index) => ( - - )) - } - - Can't get instantiation ordered state:
{ JSON.stringify(instantiationOrderStateError, null, 2) }
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + { instantiationList.map((instantiation, index) => { + return ( + + + + + + + + + + ) + }) } + +
#Instantiation NameEdit InstantiationDelete InstantiationChange Order StateInstantiation Order StateInstantiation Current State
{ index + 1 }{ instantiation.name } + + + + + { renderDeleteButton(instantiation, index) } + + + + + { instantiation.orderedState }{ instantiation.currentState }
{ alertMessage } -
+
- - + + ); diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/InstanceModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/InstanceModal.test.js.snap deleted file mode 100644 index ba7caf9..0000000 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/InstanceModal.test.js.snap +++ /dev/null @@ -1,68 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`Verify MonitoringInstantiation renders correctly 1`] = ` - - - - Change Tosca Instance Properties - - -
- -
- - Can't get service template: -
- {} -
- - Can't get instance properties: -
- {} -
- - -
- - - - - -`; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/InstancePropertiesModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/InstancePropertiesModal.test.js.snap new file mode 100644 index 0000000..96b500e --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/InstancePropertiesModal.test.js.snap @@ -0,0 +1,68 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Verify MonitoringInstantiation renders correctly 1`] = ` + + + + Create Tosca Instance Properties + + +
+ +
+ + Can't get service template: +
+ {} +
+ + Can't get instance properties: +
+ {} +
+ + +
+ + + + + +`; diff --git a/gui-clamp/ui-react/src/components/dialogs/InstantiationManagementModal.test.js b/gui-clamp/ui-react/src/components/dialogs/InstantiationManagementModal.test.js index 61f8c1a..25292d0 100644 --- a/gui-clamp/ui-react/src/components/dialogs/InstantiationManagementModal.test.js +++ b/gui-clamp/ui-react/src/components/dialogs/InstantiationManagementModal.test.js @@ -22,27 +22,27 @@ import React from "react"; import toJson from "enzyme-to-json"; import { createMemoryHistory } from "history"; import { act } from "react-dom/test-utils"; -import InstantiationManagementModal from "./ControlLoop/InstantiationManagementModal"; +import ChangeOrderStateModal from "./ControlLoop/ChangeOrderStateModal"; describe('Verify InstantiationManagementModal', () => { it("renders without crashing", () => { - shallow(); + shallow(); }); it("renders correctly", () => { - const tree = shallow(); + const tree = shallow(); expect(toJson(tree)).toMatchSnapshot(); }); it('should have save button element', () => { - const container = shallow() + const container = shallow() expect(container.find('[variant="primary"]').length).toEqual(1); }); it('handleSave called when save button clicked', () => { const history = createMemoryHistory(); - const component = mount() + const component = mount() const logSpy = jest.spyOn(console, 'log'); act(() => { @@ -52,13 +52,13 @@ describe('Verify InstantiationManagementModal', () => { }); it('should have close button element', () => { - const container = shallow() + const container = shallow() expect(container.find('[variant="secondary"]').length).toEqual(1); }); it('handleClose called when close button clicked', () => { const history = createMemoryHistory(); - const component = mount() + const component = mount() const logSpy = jest.spyOn(console, 'log'); act(() => { diff --git a/gui-clamp/ui-react/src/components/menu/MenuBar.js b/gui-clamp/ui-react/src/components/menu/MenuBar.js index 99480a9..59cff22 100644 --- a/gui-clamp/ui-react/src/components/menu/MenuBar.js +++ b/gui-clamp/ui-react/src/components/menu/MenuBar.js @@ -115,9 +115,7 @@ export default class MenuBar extends React.Component { Instantiation - Monitor Instantiation - Manage Instantiation - Edit Control Loop Instance Properties + Instantiation Management Commissioning Manage Commissioned Tosca Template diff --git a/gui-clamp/ui-react/src/components/menu/__snapshots__/MenuBar.test.js.snap b/gui-clamp/ui-react/src/components/menu/__snapshots__/MenuBar.test.js.snap index 36132ff..dbae793 100644 --- a/gui-clamp/ui-react/src/components/menu/__snapshots__/MenuBar.test.js.snap +++ b/gui-clamp/ui-react/src/components/menu/__snapshots__/MenuBar.test.js.snap @@ -1071,139 +1071,9 @@ exports[`Verify MenuBar Test the render method 1`] = ` } } disabled={false} - to="/monitorInstantiation" + to="/instantiationManagement" > - Monitor Instantiation - - - Manage Instantiation - - - Edit Control Loop Instance Properties + Instantiation Management