diff options
11 files changed, 566 insertions, 1 deletions
diff --git a/gui-clamp/ui-react-lib/libIndex.js b/gui-clamp/ui-react-lib/libIndex.js index 2af68db..779e363 100644 --- a/gui-clamp/ui-react-lib/libIndex.js +++ b/gui-clamp/ui-react-lib/libIndex.js @@ -60,6 +60,8 @@ export { default as InstantiationItem } from '../ui-react/src/components/dialogs export { default as InstantiationElements } from '../ui-react/src/components/dialogs/ControlLoop/InstantiationElements'; export { default as InstantiationElementItem } from '../ui-react/src/components/dialogs/ControlLoop/InstantiationElementItem'; export { default as InstanceModal } from '../ui-react/src/components/dialogs/ControlLoop/InstanceModal'; +export { default as InstantiationManagementModal } from '../ui-react/src/components/dialogs/ControlLoop/InstantiationManagementModal'; +export { default as InstantiationOrderStateChangeItem } from '../ui-react/src/components/dialogs/ControlLoop/InstantiationOrderStateChangeItem'; export { default as ControlLoopService } from '../ui-react/src/api/ControlLoopService'; export { default as GetLocalToscaFileForUpload } from '../ui-react/src/components/dialogs/ControlLoop/GetLocalToscaFileForUpload'; export { default as ReadAndConvertYaml } from '../ui-react/src/components/dialogs/ControlLoop/ReadAndConvertYaml'; diff --git a/gui-clamp/ui-react/src/LoopUI.js b/gui-clamp/ui-react/src/LoopUI.js index bfdd7dd..79943bf 100644 --- a/gui-clamp/ui-react/src/LoopUI.js +++ b/gui-clamp/ui-react/src/LoopUI.js @@ -56,6 +56,7 @@ import MonitorInstantiation from "./components/dialogs/ControlLoop/MonitorInstan import GetLocalToscaFileForUpload from "./components/dialogs/ControlLoop/GetLocalToscaFileForUpload"; import CommissioningModal from "./components/dialogs/ControlLoop/CommissioningModal"; import InstanceModal from "./components/dialogs/ControlLoop/InstanceModal"; +import InstantiationManagementModal from "./components/dialogs/ControlLoop/InstantiationManagementModal"; const StyledMainDiv = styled.div` background-color: ${ props => props.theme.backgroundColor }; @@ -386,6 +387,7 @@ export default class LoopUI extends React.Component { showFailAlert={ this.showFailAlert }/>) } /> <Route path="/monitorInstantiation" render={ (routeProps) => (<MonitorInstantiation { ...routeProps } />) }/> + <Route path="/manageInstantiation" render={ (routeProps) => (<InstantiationManagementModal { ...routeProps } />) }/> <Route path="/editControlLoopInstanceProperties" render={ (routeProps) => (<InstanceModal { ...routeProps } />) }/> </React.Fragment> ); 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 2123f37..abfcb8d 100644 --- a/gui-clamp/ui-react/src/__snapshots__/LoopUI.test.js.snap +++ b/gui-clamp/ui-react/src/__snapshots__/LoopUI.test.js.snap @@ -90,6 +90,10 @@ exports[`Verify LoopUI Test the render method 1`] = ` render={[Function]} /> <Route + path="/manageInstantiation" + render={[Function]} + /> + <Route path="/editControlLoopInstanceProperties" render={[Function]} /> diff --git a/gui-clamp/ui-react/src/__snapshots__/OnapClamp.test.js.snap b/gui-clamp/ui-react/src/__snapshots__/OnapClamp.test.js.snap index d9cf3d3..a87127e 100644 --- a/gui-clamp/ui-react/src/__snapshots__/OnapClamp.test.js.snap +++ b/gui-clamp/ui-react/src/__snapshots__/OnapClamp.test.js.snap @@ -119,6 +119,10 @@ exports[`Verify OnapClamp Test the render method 1`] = ` render={[Function]} /> <Route + path="/manageInstantiation" + render={[Function]} + /> + <Route path="/editControlLoopInstanceProperties" render={[Function]} /> diff --git a/gui-clamp/ui-react/src/api/ControlLoopService.js b/gui-clamp/ui-react/src/api/ControlLoopService.js index 4a2fdbd..0d6e11c 100644 --- a/gui-clamp/ui-react/src/api/ControlLoopService.js +++ b/gui-clamp/ui-react/src/api/ControlLoopService.js @@ -41,6 +41,27 @@ export default class ControlLoopService { return response } + static async getInstanceOrderState(windowLocationPathName) { + const response = await fetch(windowLocationPathName + '/restservices/clds/v2/toscaControlLoop/getInstantiationOrderState'); + + const data = await response; + + return data; + } + + static async changeInstanceOrderState(toscaObject, windowLocationPathName) { + const response = await fetch(windowLocationPathName + '/restservices/clds/v2/toscaControlLoop/putToscaInstantiationStateChange', { + method: 'PUT', + headers: { + "Content-Type": "application/json" + }, + credentials: 'same-origin', + body: JSON.stringify(toscaObject) + }); + + return response + } + static async getToscaTemplate(name, version, windowLocationPathname) { const params = { name: name, diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationManagementModal.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationManagementModal.js new file mode 100644 index 0000000..c6c1eb9 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationManagementModal.js @@ -0,0 +1,170 @@ +/* + * ============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 styled from "styled-components"; +import Modal from "react-bootstrap/Modal"; +import Button from "react-bootstrap/Button"; +import React, { useContext, useEffect, useRef, useState } from "react"; +import InstantiationOrderStateChangeItem from "./InstantiationOrderStateChangeItem"; +import ControlLoopService from "../../../api/ControlLoopService"; +import { Alert, Container, Dropdown } from "react-bootstrap"; + +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 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 [alertMessage, setAlertMessage] = useState(null); + + useEffect(async () => { + setWindowLocationPathnameGet(window.location.pathname); + + const instantiationOrderState = await ControlLoopService.getInstanceOrderState(windowLocationPathnameGet) + .catch(error => error.message); + + const orderStateJson = await instantiationOrderState.json(); + + if (!instantiationOrderState.ok || orderStateJson['controlLoopIdentifierList'].length === 0) { + setInstantiationOrderStateOk(true); + setInstantiationOrderStateError(orderStateJson); + } else { + setControlLoopIdentifierList(orderStateJson['controlLoopIdentifierList']); + setOrderedState(orderStateJson['orderedState']); + } + }, []); + + const handleDropSelect = (event) => { + console.log("handleDropDownChange called"); + + const stateChangeObject = { + orderedState: event, + controlLoopIdentifierList: controlLoopIdentifierList + } + setToscaOrderStateObject(stateChangeObject); + orderStateContext.orderState = stateChangeObject; + } + + 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(<Alert variant="success"> + <Alert.Heading>Order State Changed Success</Alert.Heading> + <p>Order State Changed was successfully changed</p> + <hr/> + </Alert>); + } + + const errorAlert = async (response) => { + console.log("errorAlert called"); + setAlertMessage(<Alert variant="danger"> + <Alert.Heading>Order State Changed Failure</Alert.Heading> + <p>An error occurred while trying to change order state</p> + <p>Status code: { await response.status } : { response.statusText }</p> + <p>Status Text: { await response.text() }</p> + <hr/> + </Alert>); + } + + return ( + <ModalStyled size="sm" + show={ show } + onHide={ handleClose } + backdrop="static" + keyboard={ false }> + <Modal.Header closeButton> + <Modal.Title>Manage Instantiation</Modal.Title> + </Modal.Header> + <div style={ { padding: '5px 5px 0 5px' } }> + <Modal.Body> + <Container> + <Dropdown onSelect={ handleDropSelect }> + <Dropdown.Toggle variant="dark" id="dropdown-basic"> + Select Order State + </Dropdown.Toggle> + <Dropdown.Menu> + <Dropdown.Item eventKey="UNINITIALISED">UNINITIALISED</Dropdown.Item> + <Dropdown.Item eventKey="PASSIVE">PASSIVE</Dropdown.Item> + <Dropdown.Item eventKey="RUNNING">RUNNING</Dropdown.Item> + </Dropdown.Menu> + </Dropdown> + { + controlLoopIdentifierList.map((clIdList, index) => ( + <InstantiationOrderStateChangeItem title={ clIdList.name } index={ index } key={ index } /> + )) + } + </Container> + <AlertStyled show={ !instantiationOrderStateOk } + variant="danger">Can't get instantiation ordered state:<br/>{ JSON.stringify(instantiationOrderStateError, null, 2) }</AlertStyled> + </Modal.Body> + <DivWhiteSpaceStyled> + { alertMessage } + </DivWhiteSpaceStyled> + </div> + <Modal.Footer> + <Button variant="primary" onClick={ handleSave }>Save</Button> + <Button variant="secondary" onClick={ handleClose }>Close</Button> + </Modal.Footer> + </ModalStyled> + ); +} + +export default InstantiationManagementModal; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationOrderStateChangeItem.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationOrderStateChangeItem.js new file mode 100644 index 0000000..8545943 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationOrderStateChangeItem.js @@ -0,0 +1,113 @@ +/* + * ============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 from "react"; +import styled from "styled-components"; + +const UninitialisedBox = styled.div` + margin: 2px -15px; + padding: 8px; + outline: none; + font-size: 16px; + font-weight: normal; + background: #cccccc; + border-radius: 8px; + border: 1px solid #7f7f7f; + + &:focus, &:active, &:after { + outline: none; + border-radius: 8px; + } +` + +const PassiveBox = styled.div` + margin: 2px -15px; + padding: 8px; + outline: none; + font-size: 16px; + font-weight: normal; + background: #ffe87c; + border-radius: 8px; + border: 1px solid #7f7f7f; + + &:focus, &:active, &:after { + outline: none; + border-radius: 8px; + } +` + +const RunningBox = styled.div` + margin: 2px -15px; + padding: 8px; + outline: none; + font-size: 16px; + font-weight: normal; + background: #7ec699; + border-radius: 8px; + border: 1px solid #7f7f7f; + + &:focus, &:active, &:after { + outline: none; + border-radius: 8px; + } +` + +const InstantiationOrderStateChangeItem = (props) => { + + const renderOrderStateItem = () => { + console.log("renderOrderStateItem called"); + switch (props.orderState) { + case 'UNINITIALISED': + console.log("called UNINITIALISED"); + return renderUninitialisedOrderedState(); + case 'PASSIVE': + console.log("called PASSIVE"); + return renderPassiveOrderedState(); + case 'RUNNING': + console.log("called RUNNING"); + return renderRunningOrderedState(); + } + } + + const renderUninitialisedOrderedState = () => { + return ( + <UninitialisedBox>{ props.title }</UninitialisedBox> + ) + } + + const renderPassiveOrderedState = () => { + return ( + <PassiveBox>{ props.title }</PassiveBox> + ) + } + + const renderRunningOrderedState = () => { + return ( + <RunningBox>{ props.title }</RunningBox> + ) + } + + return ( + <React.Fragment> + { renderOrderStateItem() } + </React.Fragment> + ); +} + +export default InstantiationOrderStateChangeItem; diff --git a/gui-clamp/ui-react/src/components/dialogs/InstantiationManagementModal.test.js b/gui-clamp/ui-react/src/components/dialogs/InstantiationManagementModal.test.js new file mode 100644 index 0000000..61f8c1a --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/InstantiationManagementModal.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 toJson from "enzyme-to-json"; +import { createMemoryHistory } from "history"; +import { act } from "react-dom/test-utils"; +import InstantiationManagementModal from "./ControlLoop/InstantiationManagementModal"; + +describe('Verify InstantiationManagementModal', () => { + + it("renders without crashing", () => { + shallow(<InstantiationManagementModal />); + }); + + it("renders correctly", () => { + const tree = shallow(<InstantiationManagementModal />); + expect(toJson(tree)).toMatchSnapshot(); + }); + + it('should have save button element', () => { + const container = shallow(<InstantiationManagementModal/>) + expect(container.find('[variant="primary"]').length).toEqual(1); + }); + + it('handleSave called when save button clicked', () => { + const history = createMemoryHistory(); + const component = mount(<InstantiationManagementModal history={ history }/>) + const logSpy = jest.spyOn(console, 'log'); + + act(() => { + component.find('[variant="primary"]').simulate('click'); + expect(logSpy).toHaveBeenCalledWith('handleSave called'); + }); + }); + + it('should have close button element', () => { + const container = shallow(<InstantiationManagementModal/>) + expect(container.find('[variant="secondary"]').length).toEqual(1); + }); + + it('handleClose called when close button clicked', () => { + const history = createMemoryHistory(); + const component = mount(<InstantiationManagementModal history={ history }/>) + 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/__snapshots__/InstantiationManagementModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/__snapshots__/InstantiationManagementModal.test.js.snap new file mode 100644 index 0000000..fde9ba1 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/__snapshots__/InstantiationManagementModal.test.js.snap @@ -0,0 +1,114 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Verify InstantiationManagementModal renders correctly 1`] = ` +<Styled(Modal) + backdrop="static" + keyboard={false} + onHide={[Function]} + show={true} + size="sm" +> + <ModalHeader + closeButton={true} + closeLabel="Close" + > + <ModalTitle> + Manage Instantiation + </ModalTitle> + </ModalHeader> + <div + style={ + Object { + "padding": "5px 5px 0 5px", + } + } + > + <ModalBody> + <Container + fluid={false} + > + <Dropdown + navbar={false} + onSelect={[Function]} + > + <DropdownToggle + id="dropdown-basic" + variant="dark" + > + Select Order State + </DropdownToggle> + <DropdownMenu + align="left" + alignRight={false} + flip={true} + > + <DropdownItem + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "render": [Function], + } + } + disabled={false} + eventKey="UNINITIALISED" + > + UNINITIALISED + </DropdownItem> + <DropdownItem + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "render": [Function], + } + } + disabled={false} + eventKey="PASSIVE" + > + PASSIVE + </DropdownItem> + <DropdownItem + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "render": [Function], + } + } + disabled={false} + eventKey="RUNNING" + > + RUNNING + </DropdownItem> + </DropdownMenu> + </Dropdown> + </Container> + <Styled(Alert) + show={false} + variant="danger" + > + Can't get instantiation ordered state: + <br /> + {} + </Styled(Alert)> + </ModalBody> + <styled.div /> + </div> + <ModalFooter> + <Button + active={false} + disabled={false} + onClick={[Function]} + variant="primary" + > + Save + </Button> + <Button + active={false} + disabled={false} + onClick={[Function]} + variant="secondary" + > + Close + </Button> + </ModalFooter> +</Styled(Modal)> +`; diff --git a/gui-clamp/ui-react/src/components/menu/MenuBar.js b/gui-clamp/ui-react/src/components/menu/MenuBar.js index 7ee7ffd..99480a9 100644 --- a/gui-clamp/ui-react/src/components/menu/MenuBar.js +++ b/gui-clamp/ui-react/src/components/menu/MenuBar.js @@ -116,8 +116,9 @@ export default class MenuBar extends React.Component { <StyledNavDropdown title="TOSCA Control Loop"> <NavDropdown.Header>Instantiation</NavDropdown.Header> <NavDropdown.Item as={ StyledLink } to="/monitorInstantiation">Monitor Instantiation</NavDropdown.Item> + <NavDropdown.Item as={ StyledLink } to="/manageInstantiation">Manage Instantiation</NavDropdown.Item> <NavDropdown.Item as={ StyledLink } to="/editControlLoopInstanceProperties">Edit Control Loop Instance Properties</NavDropdown.Item> - <NavDropdown.Divider /> + <NavDropdown.Divider/> <NavDropdown.Header>Commissioning</NavDropdown.Header> <NavDropdown.Item as={ StyledLink } to="/readToscaTemplate">Manage Commissioned Tosca Template</NavDropdown.Item> <NavDropdown.Item as={ StyledLink } to="/uploadToscaFile">Upload Tosca to Commissioning</NavDropdown.Item> 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 edc0624..36132ff 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 @@ -1136,6 +1136,71 @@ exports[`Verify MenuBar Test the render method 1`] = ` } } disabled={false} + to="/manageInstantiation" + > + Manage Instantiation + </DropdownItem> + <DropdownItem + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "attrs": Array [], + "componentStyle": e { + "baseHash": -715527839, + "baseStyle": undefined, + "componentId": "sc-bdnxRM", + "isStatic": false, + "rules": Array [ + " + color: ", + [Function], + "; + background-color: ", + [Function], + "; + font-weight: normal; + display: block; + width: 100%; + padding: .25rem 1.5rem; + clear: both; + text-align: inherit; + white-space: nowrap; + border: 0; + + :hover { + text-decoration: none; + background-color: ", + [Function], + "; + color: ", + [Function], + "; + } +", + ], + "staticRulesId": "", + }, + "foldedComponentIds": Array [], + "render": [Function], + "shouldForwardProp": undefined, + "styledComponentId": "sc-bdnxRM", + "target": Object { + "$$typeof": Symbol(react.forward_ref), + "propTypes": Object { + "innerRef": [Function], + "onClick": [Function], + "replace": [Function], + "target": [Function], + "to": [Function], + }, + "render": [Function], + }, + "toString": [Function], + "warnTooManyClasses": [Function], + "withComponent": [Function], + } + } + disabled={false} to="/editControlLoopInstanceProperties" > Edit Control Loop Instance Properties |