diff options
author | brunomilitzer <bruno.militzer@est.tech> | 2021-07-08 15:33:55 +0100 |
---|---|---|
committer | brunomilitzer <bruno.militzer@est.tech> | 2021-07-13 15:20:41 +0100 |
commit | 71d6358a8f787c5d2688a485d42ff9514dc58a56 (patch) | |
tree | 091e1bf68ecff15267d7c1c14f68cca9d04f38a8 | |
parent | 9752dad7c9ad8f970fb87b92c06c7ae8d8121280 (diff) |
Add Upload Control Loop Instantiation
Created Functionality to Upload JSON file to Control Loop Instantiation.
Issue-ID: POLICY-3436
Change-Id: Iefd538c91154b7e61615ab63b440378e2feea502
Signed-off-by: brunomilitzer <bruno.militzer@est.tech>
21 files changed, 746 insertions, 32 deletions
diff --git a/gui-clamp/ui-react-lib/libIndex.js b/gui-clamp/ui-react-lib/libIndex.js index 179b882..b0981b0 100644 --- a/gui-clamp/ui-react-lib/libIndex.js +++ b/gui-clamp/ui-react-lib/libIndex.js @@ -55,7 +55,11 @@ export { default as ViewAllPolicies } from '../ui-react/src/components/dialogs/P export { default as PolicyDeploymentEditor } from '../ui-react/src/components/dialogs/Policy/PolicyDeploymentEditor'; export { default as PoliciesTreeViewer } from '../ui-react/src/components/dialogs/Policy/PoliciesTreeViewer'; export { default as PolicyToscaFileSelector } from '../ui-react/src/components/dialogs/Policy/PolicyToscaFileSelector'; -export { default as MonitoringControlLoopModal } from '../ui-react/src/components/dialogs/ControlLoop/MonitoringControlLoopModal'; +export { default as MonitorInstantiation } from '../ui-react/src/components/dialogs/ControlLoop/MonitorInstantiation'; +export { default as InstantiationItem } from '../ui-react/src/components/dialogs/ControlLoop/InstantiationItem'; +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 UploadToscaInstantiation } from '../ui-react/src/components/dialogs/ControlLoop/UploadToscaInstantiation'; 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/package.json b/gui-clamp/ui-react/package.json index 5754d44..6b67e3f 100644 --- a/gui-clamp/ui-react/package.json +++ b/gui-clamp/ui-react/package.json @@ -47,7 +47,7 @@ "@babel/preset-env": "7.14.7", "@babel/preset-react": "7.14.5", "@wojtekmaj/enzyme-adapter-react-17": "0.6.2", - "babel-jest": "27.0.6", + "babel-jest": "26.6.0", "enzyme": "3.11.0", "enzyme-adapter-react-17-updated": "1.0.2", "enzyme-to-json": "3.6.2", diff --git a/gui-clamp/ui-react/src/LoopUI.js b/gui-clamp/ui-react/src/LoopUI.js index 752c89d..109a0b3 100644 --- a/gui-clamp/ui-react/src/LoopUI.js +++ b/gui-clamp/ui-react/src/LoopUI.js @@ -52,8 +52,9 @@ import Spinner from 'react-bootstrap/Spinner'; import { Link } from 'react-router-dom'; import ReadAndConvertYaml from "./components/dialogs/ControlLoop/ReadAndConvertYaml"; -import MonitoringControlLoopModal from "./components/dialogs/ControlLoop/MonitoringControlLoopModal"; +import MonitorInstantiation from "./components/dialogs/ControlLoop/MonitorInstantiation"; import GetLocalToscaFileForUpload from "./components/dialogs/ControlLoop/GetLocalToscaFileForUpload"; +import UploadToscaInstantiation from "./components/dialogs/ControlLoop/UploadToscaInstantiation"; const StyledMainDiv = styled.div` background-color: ${ props => props.theme.backgroundColor }; @@ -382,7 +383,8 @@ export default class LoopUI extends React.Component { showSucAlert={ this.showSucAlert } showFailAlert={ this.showFailAlert }/>) } /> - <Route path="/monitoring" render={ (routeProps) => (<MonitoringControlLoopModal { ...routeProps } />) }/> + <Route path="/monitorInstantiation" render={ (routeProps) => (<MonitorInstantiation { ...routeProps } />) }/> + <Route path="/uploadToscaInstantiation" render={ (routeProps) => (<UploadToscaInstantiation { ...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 6d25823..040fdaa 100644 --- a/gui-clamp/ui-react/src/__snapshots__/LoopUI.test.js.snap +++ b/gui-clamp/ui-react/src/__snapshots__/LoopUI.test.js.snap @@ -82,7 +82,11 @@ exports[`Verify LoopUI Test the render method 1`] = ` render={[Function]} /> <Route - path="/monitoring" + path="/monitorInstantiation" + render={[Function]} + /> + <Route + path="/uploadToscaInstantiation" render={[Function]} /> <div /> 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 f5680b1..5d8a207 100644 --- a/gui-clamp/ui-react/src/__snapshots__/OnapClamp.test.js.snap +++ b/gui-clamp/ui-react/src/__snapshots__/OnapClamp.test.js.snap @@ -111,7 +111,11 @@ exports[`Verify OnapClamp Test the render method 1`] = ` render={[Function]} /> <Route - path="/monitoring" + path="/monitorInstantiation" + render={[Function]} + /> + <Route + path="/uploadToscaInstantiation" render={[Function]} /> <div /> diff --git a/gui-clamp/ui-react/src/api/ControlLoopService.js b/gui-clamp/ui-react/src/api/ControlLoopService.js index 1882f78..30b0522 100644 --- a/gui-clamp/ui-react/src/api/ControlLoopService.js +++ b/gui-clamp/ui-react/src/api/ControlLoopService.js @@ -19,7 +19,7 @@ export default class ControlLoopService { - static async getControlLoopList(windowLocationPathname) { + static async fetchControlLoopInstantiation(windowLocationPathname) { return await fetch(windowLocationPathname + '/restservices/clds/v2/toscaControlLoop/getToscaInstantiation', { method: 'GET', @@ -28,10 +28,10 @@ export default class ControlLoopService { }, credentials: 'same-origin', }).then(response => { - console.log("getControlLoopList received " + response.status); + console.log("fetchControlLoopInstantiation received " + response.status); if (response.ok) { - console.info("getControlLoopList query successful"); + console.info("fetchControlLoopInstantiation query successful"); return response.json(); } else { return response.text().then(responseBody => { @@ -39,12 +39,26 @@ export default class ControlLoopService { }); } }).catch(error => { - console.error("getControlLoopList error occurred ", error); - alert("getControlLoopList error occurred " + error); + console.error("fetchControlLoopInstantiation error occurred ", error); + alert("fetchControlLoopInstantiation error occurred " + error); return undefined; }); } + static async uploadToscaInstantiation(toscaObject, windowLocationPathname) { + + const response = await fetch(windowLocationPathname + '/restservices/clds/v2/toscaControlLoop/postToscaInstantiation', { + method: 'POST', + 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/ControlLoopElementItem.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationElementItem.js index 632cacd..0e2c407 100644 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ControlLoopElementItem.js +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationElementItem.js @@ -65,7 +65,7 @@ const ButtonStyle = styled(Button)` } ` -const ControlLoopElementItem = (props) => { +const InstantiationElementItem = (props) => { const [title, setTitle] = useState(""); useEffect(() => { @@ -121,4 +121,4 @@ const ControlLoopElementItem = (props) => { ); } -export default ControlLoopElementItem; +export default InstantiationElementItem; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ControlLoopElements.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationElements.js index 57bdf40..d8e6348 100644 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ControlLoopElements.js +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationElements.js @@ -18,9 +18,9 @@ */ import React, { useEffect, useState } from "react"; -import ControlLoopElementItem from "./ControlLoopElementItem"; +import InstantiationElementItem from "./InstantiationElementItem"; -const ControlLoopElements = (props) => { +const InstantiationElements = (props) => { const [clElements, setClElements] = useState([]); useEffect(() => { @@ -31,11 +31,11 @@ const ControlLoopElements = (props) => { <React.Fragment> { clElements.map((clEl, index) => ( - <ControlLoopElementItem title={ clEl["definition"]["name"] } orderedState={ clEl["orderedState"] } key={ index } /> + <InstantiationElementItem title={ clEl["definition"]["name"] } orderedState={ clEl["orderedState"] } key={ index } /> )) } </React.Fragment> ); } -export default ControlLoopElements; +export default InstantiationElements; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ControlLoopItem.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationItem.js index f149517..a59770b 100644 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ControlLoopItem.js +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/InstantiationItem.js @@ -71,7 +71,7 @@ const ToggleButton = styled(Button)` } ` -const ControlLoopItem = (props) => { +const InstantiationItem = (props) => { const toggleState = () => { switch (props.orderedState) { case 'UNINITIALISED': @@ -125,4 +125,4 @@ const ControlLoopItem = (props) => { ); } -export default ControlLoopItem; +export default InstantiationItem; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/MonitoringControlLoopModal.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/MonitorInstantiation.js index 03fb64b..428defa 100644 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/MonitoringControlLoopModal.js +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/MonitorInstantiation.js @@ -21,15 +21,15 @@ import React, { useEffect, useState } from "react"; import styled from "styled-components"; import Modal from "react-bootstrap/Modal"; import Button from "react-bootstrap/Button"; -import ControlLoopItem from "./ControlLoopItem"; +import InstantiationItem from "./InstantiationItem"; import ControlLoopService from "../../../api/ControlLoopService"; -import ControlLoopElements from "./ControlLoopElements"; +import InstantiationElements from "./InstantiationElements"; const ModalStyled = styled(Modal)` background-color: transparent; ` -const MonitoringControlLoopModal = (props) => { +const MonitorInstantiation = (props) => { const [show, setShow] = useState(true); const [controlLoopList, setControlLoopList] = useState([]); const [windowLocationPathname, setWindowLocationPathname] = useState(''); @@ -37,12 +37,13 @@ const MonitoringControlLoopModal = (props) => { useEffect(() => { setWindowLocationPathname(window.location.pathname); - ControlLoopService.getControlLoopList(windowLocationPathname).then(controlLoopList => { + ControlLoopService.fetchControlLoopInstantiation(windowLocationPathname).then(controlLoopList => { setControlLoopList(controlLoopList['controlLoopList']); }); }, []) const handleClose = () => { + console.log('handleClose called'); setShow(false); props.history.push('/'); } @@ -50,22 +51,22 @@ const MonitoringControlLoopModal = (props) => { return ( <ModalStyled size="xl" show={ show } onHide={ handleClose } backdrop="static" keyboard={ false }> <Modal.Header closeButton> - <Modal.Title>Tosca Control Loop - Monitoring</Modal.Title> + <Modal.Title>Tosca Instantiation - Monitoring</Modal.Title> </Modal.Header> <Modal.Body> { controlLoopList.map((clList, index) => ( - <ControlLoopItem title={ clList["name"] } orderedState={ clList["orderedState"] } index={ index } key={ index }> - <ControlLoopElements elements={ clList["elements"] } /> - </ControlLoopItem> + <InstantiationItem title={ clList["name"] } orderedState={ clList["orderedState"] } index={ index } key={ index }> + <InstantiationElements elements={ clList["elements"] } /> + </InstantiationItem> )) } </Modal.Body> <Modal.Footer> - <Button variant="secondary" type="null" onClick={ handleClose }>Cancel</Button> + <Button variant="secondary" type="null" onClick={ handleClose }>Close</Button> </Modal.Footer> </ModalStyled> ) } -export default MonitoringControlLoopModal; +export default MonitorInstantiation; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/MonitorInstantiation.test.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/MonitorInstantiation.test.js new file mode 100644 index 0000000..5e30924 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/MonitorInstantiation.test.js @@ -0,0 +1,64 @@ +/* + * ============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 toJson from "enzyme-to-json"; +import { createMemoryHistory } from "history"; +import React from "react"; +import MonitorInstantiation from "./MonitorInstantiation"; +import { act } from "react-dom/test-utils"; + +describe('Verify MonitoringInstantiation', () => { + + it("renders without crashing", () => { + shallow(<MonitorInstantiation/>); + }); + + it("renders correctly", () => { + const tree = shallow(<MonitorInstantiation/>); + expect(toJson(tree)).toMatchSnapshot(); + }); + + it('should have a Button element', () => { + const container = shallow(<MonitorInstantiation/>) + expect(container.find('Button').length).toEqual(1); + }); + + it('handleClose called when bottom button clicked', () => { + const history = createMemoryHistory(); + const component = mount(<MonitorInstantiation history={ history }/>) + const logSpy = jest.spyOn(console, 'log'); + + act(() => { + component.find('[variant="secondary"]').simulate('click'); + expect(logSpy).toHaveBeenCalledWith('handleClose called'); + }); + }); + + it('handleClose called when top-right button clicked', () => { + const history = createMemoryHistory(); + const component = mount(<MonitorInstantiation history={ history }/>) + const logSpy = jest.spyOn(console, 'log'); + + act(() => { + component.find('[size="xl"]').get(0).props.onHide(); + expect(logSpy).toHaveBeenCalledWith('handleClose called'); + }); + }); +}); diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaInstantiation.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaInstantiation.js new file mode 100644 index 0000000..d9bbe8b --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaInstantiation.js @@ -0,0 +1,136 @@ +/* + * ============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, { useState } from "react"; +import Modal from "react-bootstrap/Modal"; +import Button from "react-bootstrap/Button"; +import styled from "styled-components"; +import Row from "react-bootstrap/Row"; +import Form from "react-bootstrap/Form"; +import UploadToscaInstantiationFile from "./UploadToscaInstantiationFile"; +import jsYaml from "js-yaml"; +import Alert from "react-bootstrap/Alert"; + +const ModalStyled = styled(Modal)` + background-color: transparent; +` +const StyledMessagesDiv = styled.div` + overflow: auto; + min-width: 100%; + max-height: 300px; + padding: 5px 5px 0px 5px; + text-align: center; +` + +const UploadToscaInstantiation = (props) => { + const [show, setShow] = useState(true); + const [fileIsSelected, setFileIsSelected] = useState(false); + const [selectedFile, setSelectedFile] = useState(); + const [jsonObject, setJsonObject] = useState([]); + const [alertMessages, setAlertMessages] = useState(); + + const fileUploadHandler = (event) => { + event.preventDefault(); + console.log('fileUploadHandler called'); + + const file = event.currentTarget.files[0]; + + if (file !== undefined) { + console.log('fileDefined called'); + setSelectedFile(file); + setFileIsSelected(true); + + const fileReader = new FileReader(); + fileReader.onload = () => { + const jsonFile = jsYaml.load(fileReader.result, 'utf8') + setJsonObject(jsonFile) + } + + fileReader.readAsText(file); + } else { + console.log('fileUndefined called'); + } + } + + const onResponseReceivedHandler = async (response) => { + console.log('onResponseReceivedHandler called'); + + if (await response.ok) { + setAlertMessages(<Alert variant="success"> + <Alert.Heading>Upload Success</Alert.Heading> + <p>Tosca Instantiation from { selectedFile.name } was Successfully Uploaded</p> + <hr/> + <p>Type: { selectedFile.type }</p><p>Size: { +selectedFile.size / 1000 }Kb</p> + </Alert>); + } else { + setAlertMessages(<Alert variant="danger"> + <Alert.Heading>Upload Failure</Alert.Heading> + <p>Tosca Instantiation from { selectedFile.name } failed to upload</p> + <p>Status code: { await response.status }: { response.statusText }</p> + <p>Response Text: { await response.text() }</p> + <hr/> + <p>Type: { selectedFile.type }</p><p>Size: { +selectedFile.size / 1000 }Kb</p> + </Alert>); + } + } + + const handleClose = () => { + console.log('handleClose called'); + setShow(false); + props.history.push('/'); + } + + return ( + <ModalStyled size="lg" show={ show } onHide={ handleClose } backdrop="static" keyboard={ false }> + <Modal.Header closeButton> + <Modal.Title>Tosca Control Loop - Create Instantiation</Modal.Title> + </Modal.Header> + <div style={ { padding: '5px 5px 0px 5px' } }> + <Modal.Body> + <Form style={ { paddingTop: '20px' } }> + <Form.Group as={ Row }> + <Form.File + type="file" + className="custom-file-label" + id="inputGroupFile01" + onChange={ fileUploadHandler } + custom + accept=".yaml,.yml,.json" + label={ fileIsSelected ? selectedFile.name : 'Please select a file' }></Form.File> + <UploadToscaInstantiationFile + jsonObject={jsonObject} + onResponseReceived={onResponseReceivedHandler}/> + <Form.Text>Only .yaml, .yml and .json files are supported</Form.Text> + </Form.Group> + <Form.Group as={ Row }> + <StyledMessagesDiv> + { alertMessages } + </StyledMessagesDiv> + </Form.Group> + </Form> + </Modal.Body> + </div> + <Modal.Footer> + <Button variant="secondary" type="null" onClick={ handleClose }>Close</Button> + </Modal.Footer> + </ModalStyled> + ) +} + +export default UploadToscaInstantiation; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaInstantiation.test.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaInstantiation.test.js new file mode 100644 index 0000000..79daafb --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaInstantiation.test.js @@ -0,0 +1,102 @@ +/* + * ============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 UploadToscaInstantiation from "./UploadToscaInstantiation"; +import { createMemoryHistory } from "history"; +import { act } from "react-dom/test-utils"; + +describe("Verify UploadToscaInstantiation", () => { + const fs = require("fs"); + const jsonFile = fs.readFileSync("src/components/dialogs/Policy/toscaInstantiationData.test.json"); + const file = new Blob([jsonFile], { type: 'file' }); + + it("renders without crashing", () => { + shallow(<UploadToscaInstantiation/>); + }); + + it("renders correctly", () => { + const tree = shallow(<UploadToscaInstantiation/>); + expect(toJson(tree)).toMatchSnapshot(); + }); + + it('should have a Button element', () => { + const container = shallow(<UploadToscaInstantiation/>) + expect(container.find('Button').length).toEqual(1); + }); + + it('handleClose called when bottom button clicked', () => { + const history = createMemoryHistory(); + const component = mount(<UploadToscaInstantiation history={ history }/>) + const logSpy = jest.spyOn(console, 'log'); + + + act(() => { + component.find('[variant="secondary"]').simulate('click'); + expect(logSpy).toHaveBeenCalledWith('handleClose called'); + }); + }); + + it('handleClose called when top-right button clicked', () => { + const history = createMemoryHistory(); + const component = mount(<UploadToscaInstantiation history={ history }/>) + const logSpy = jest.spyOn(console, 'log'); + + + act(() => { + component.find('[size="lg"]').get(0).props.onHide(); + expect(logSpy).toHaveBeenCalledWith('handleClose called'); + }); + }); + + it('fileUploadHandler called when uploading a defined file', () => { + const component = mount(<UploadToscaInstantiation/>) + const logSpy = jest.spyOn(console, 'log'); + + const event = { + preventDefault() { + }, + currentTarget: { files: [file] } + }; + + act(() => { + component.find('[type="file"]').get(0).props.onChange(event); + expect(logSpy).toHaveBeenCalledWith('fileUploadHandler called'); + expect(logSpy).toHaveBeenCalledWith('fileDefined called'); + }); + }); + + it('fileUploadHandler called when uploading a undefined file', () => { + const component = mount(<UploadToscaInstantiation/>) + const logSpy = jest.spyOn(console, 'log'); + + const event = { + preventDefault() { + }, + currentTarget: { files: [] } + }; + + act(() => { + component.find('[type="file"]').get(0).props.onChange(event); + expect(logSpy).toHaveBeenCalledWith('fileUndefined called'); + }); + }); +}); diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaInstantiationFile.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaInstantiationFile.js new file mode 100644 index 0000000..5329bc0 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaInstantiationFile.js @@ -0,0 +1,52 @@ +/* + * ============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 Button from "react-bootstrap/Button"; +import ControlLoopService from "../../../api/ControlLoopService"; + +const UploadToscaInstantiationFile = (props) => { + const [windowLocationPathName, setWindowLocationPathname] = useState(''); + + const postToscaInstantiationHandler = async (event) => { + event.preventDefault(); + console.log('postToscaInstantiationHandler called'); + + setWindowLocationPathname(window.location.pathname); + + const response = await ControlLoopService.uploadToscaInstantiation(props.jsonObject, windowLocationPathName) + .catch(error => error.message); + + props.onResponseReceived(response); + } + + return ( + <React.Fragment> + <Button variant="primary" + block={ true } + type="submit" + onClick={ postToscaInstantiationHandler }> + Upload Tosca Instantiation + </Button> + </React.Fragment> + ); +} + +export default UploadToscaInstantiationFile; + diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaInstantiationFile.test.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaInstantiationFile.test.js new file mode 100644 index 0000000..83ab8d5 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaInstantiationFile.test.js @@ -0,0 +1,61 @@ +/* + * ============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 UploadToscaInstantiationFile from "./UploadToscaInstantiationFile"; +import toJson from "enzyme-to-json"; +import { act } from "react-dom/test-utils"; + +describe('Verify UploadToscaInstantiationFile', () => { + + it("renders without crashing", () => { + shallow(<UploadToscaInstantiationFile />); + }); + + it("renders correctly", () => { + const tree = shallow(<UploadToscaInstantiationFile />); + expect(toJson(tree)).toMatchSnapshot(); + }); + + it('should have a Button element', () => { + const container = shallow(<UploadToscaInstantiationFile />) + expect(container.find('Button').length).toEqual(1); + }); + + it('Button should have a specific text', () => { + const container = shallow(<UploadToscaInstantiationFile />) + expect(container.find('Button').text()).toBe('Upload Tosca Instantiation'); + }); + + it('button should call postToscaInstantiationHandler when clicked', async () => { + const mockFunction = jest.fn(() => 'default'); + const component = mount(<UploadToscaInstantiationFile onResponseReceived={mockFunction}/>) + const logSpy = jest.spyOn(console, 'log'); + const event = { + preventDefault() { + } + }; + + await act(async () => { + component.find('[variant="primary"]').get(0).props.onClick(event); + expect(logSpy).toHaveBeenCalledWith('postToscaInstantiationHandler called'); + }); + }); +}); diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/MonitorInstantiation.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/MonitorInstantiation.test.js.snap new file mode 100644 index 0000000..6504b54 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/MonitorInstantiation.test.js.snap @@ -0,0 +1,32 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Verify MonitoringInstantiation renders correctly 1`] = ` +<Styled(Modal) + backdrop="static" + keyboard={false} + onHide={[Function]} + show={true} + size="xl" +> + <ModalHeader + closeButton={true} + closeLabel="Close" + > + <ModalTitle> + Tosca Instantiation - Monitoring + </ModalTitle> + </ModalHeader> + <ModalBody /> + <ModalFooter> + <Button + active={false} + disabled={false} + onClick={[Function]} + type="null" + variant="secondary" + > + Close + </Button> + </ModalFooter> +</Styled(Modal)> +`; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/UploadToscaInstantiation.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/UploadToscaInstantiation.test.js.snap new file mode 100644 index 0000000..77e4b99 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/UploadToscaInstantiation.test.js.snap @@ -0,0 +1,91 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Verify UploadToscaInstantiation renders correctly 1`] = ` +<Styled(Modal) + backdrop="static" + keyboard={false} + onHide={[Function]} + show={true} + size="lg" +> + <ModalHeader + closeButton={true} + closeLabel="Close" + > + <ModalTitle> + Tosca Control Loop - Create Instantiation + </ModalTitle> + </ModalHeader> + <div + style={ + Object { + "padding": "5px 5px 0px 5px", + } + } + > + <ModalBody> + <Form + inline={false} + style={ + Object { + "paddingTop": "20px", + } + } + > + <FormGroup + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "defaultProps": Object { + "noGutters": false, + }, + "render": [Function], + } + } + > + <FormFile + accept=".yaml,.yml,.json" + className="custom-file-label" + custom={true} + id="inputGroupFile01" + label="Please select a file" + onChange={[Function]} + type="file" + /> + <UploadToscaInstantiationFile + jsonObject={Array []} + onResponseReceived={[Function]} + /> + <FormText> + Only .yaml, .yml and .json files are supported + </FormText> + </FormGroup> + <FormGroup + as={ + Object { + "$$typeof": Symbol(react.forward_ref), + "defaultProps": Object { + "noGutters": false, + }, + "render": [Function], + } + } + > + <styled.div /> + </FormGroup> + </Form> + </ModalBody> + </div> + <ModalFooter> + <Button + active={false} + disabled={false} + onClick={[Function]} + type="null" + variant="secondary" + > + Close + </Button> + </ModalFooter> +</Styled(Modal)> +`; diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/UploadToscaInstantiationFile.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/UploadToscaInstantiationFile.test.js.snap new file mode 100644 index 0000000..3ac5087 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/UploadToscaInstantiationFile.test.js.snap @@ -0,0 +1,16 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Verify UploadToscaInstantiationFile renders correctly 1`] = ` +<Fragment> + <Button + active={false} + block={true} + disabled={false} + onClick={[Function]} + type="submit" + variant="primary" + > + Upload Tosca Instantiation + </Button> +</Fragment> +`; diff --git a/gui-clamp/ui-react/src/components/dialogs/Policy/toscaInstantiationData.test.json b/gui-clamp/ui-react/src/components/dialogs/Policy/toscaInstantiationData.test.json new file mode 100644 index 0000000..d740203 --- /dev/null +++ b/gui-clamp/ui-react/src/components/dialogs/Policy/toscaInstantiationData.test.json @@ -0,0 +1,59 @@ +{ + "controlLoopList": [ + { + "name": "PMSHInstance0", + "version": "1.0.1", + "definition": { + "name": "org.onap.domain.pmsh.PMSHControlLoopDefinition", + "version": "1.2.3" + }, + "state": "UNINITIALISED", + "orderedState": "UNINITIALISED", + "description": "PMSH control loop instance 0", + "elements": { + "709c62b3-8918-41b9-a747-d21eb79c6c20": { + "id": "709c62b3-8918-41b9-a747-d21eb79c6c20", + "definition": { + "name": "org.onap.domain.pmsh.PMSH_DCAEMicroservice", + "version": "1.2.3" + }, + "participantType": { + "name": "org.onap.dcae.controlloop.DCAEMicroserviceControlLoopParticipant", + "version": "2.3.4" + }, + "state": "UNINITIALISED", + "orderedState": "UNINITIALISED", + "description": "DCAE Control Loop Element for the PMSH instance 0 control loop" + }, + "709c62b3-8918-41b9-a747-d21eb79c6c21": { + "id": "709c62b3-8918-41b9-a747-d21eb79c6c21", + "definition": { + "name": "org.onap.domain.pmsh.PMSH_MonitoringPolicyControlLoopElement", + "version": "1.2.3" + }, + "participantType": { + "name": "org.onap.policy.controlloop.PolicyControlLoopParticipant", + "version": "2.3.1" + }, + "state": "UNINITIALISED", + "orderedState": "UNINITIALISED", + "description": "Monitoring Policy Control Loop Element for the PMSH instance 0 control loop" + }, + "709c62b3-8918-41b9-a747-d21eb79c6c22": { + "id": "709c62b3-8918-41b9-a747-d21eb79c6c22", + "definition": { + "name": "org.onap.domain.pmsh.PMSH_OperationalPolicyControlLoopElement", + "version": "1.2.3" + }, + "participantType": { + "name": "org.onap.policy.controlloop.PolicyControlLoopParticipant", + "version": "2.3.1" + }, + "state": "UNINITIALISED", + "orderedState": "UNINITIALISED", + "description": "Operational Policy Control Loop Element for the PMSH instance 0 control loop" + } + } + } + ] +} diff --git a/gui-clamp/ui-react/src/components/menu/MenuBar.js b/gui-clamp/ui-react/src/components/menu/MenuBar.js index 4574a2a..f32d6c8 100644 --- a/gui-clamp/ui-react/src/components/menu/MenuBar.js +++ b/gui-clamp/ui-react/src/components/menu/MenuBar.js @@ -114,7 +114,9 @@ export default class MenuBar extends React.Component { <NavDropdown.Item as={ StyledLink } to="/undeploy" disabled={ this.state.disabled }>UnDeploy to DCAE (UNDEPLOY)</NavDropdown.Item> </StyledNavDropdown> <StyledNavDropdown title="TOSCA Control Loop"> - <NavDropdown.Item as={ StyledLink } to="/monitoring">Monitoring Control Loop</NavDropdown.Item> + <NavDropdown.Header>Instantiation</NavDropdown.Header> + <NavDropdown.Item as={ StyledLink } to="/monitorInstantiation">Monitor Instantiation</NavDropdown.Item> + <NavDropdown.Item as={ StyledLink } to="/uploadToscaInstantiation">Upload Tosca Instantiation</NavDropdown.Item> <NavDropdown.Divider /> <NavDropdown.Header>Commissioning</NavDropdown.Header> <NavDropdown.Item as={ StyledLink } to="/readToscaTemplate">View Tosca Template</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 2712218..9543c12 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 @@ -1005,6 +1005,76 @@ exports[`Verify MenuBar Test the render method 1`] = ` <Styled(NavDropdown) title="TOSCA Control Loop" > + <DropdownHeader + role="heading" + > + Instantiation + </DropdownHeader> + <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="/monitorInstantiation" + > + Monitor Instantiation + </DropdownItem> <DropdownItem as={ Object { @@ -1066,9 +1136,9 @@ exports[`Verify MenuBar Test the render method 1`] = ` } } disabled={false} - to="/monitoring" + to="/uploadToscaInstantiation" > - Monitoring Control Loop + Upload Tosca Instantiation </DropdownItem> <DropdownDivider role="separator" |