From 6687b48b58bf1248532de48f3e375b1f663f4015 Mon Sep 17 00:00:00 2001
From: "saul.gill" <saul.gill@est.tech>
Date: Fri, 2 Jul 2021 17:10:54 +0100
Subject: Changed components to be service-centric

Moved endpoints to ControlLoopService.js
Refactored components and tests

Issue-ID: POLICY-3424
Change-Id: I70d48750250eecd651b845ef0c726617983f75f5
Signed-off-by: saul.gill <saul.gill@est.tech>
---
 .../ControlLoop/GetLocalToscaFileForUpload.js      | 150 +++++++++++++++++++++
 .../ControlLoop/GetLocalToscaFileForUpload.test.js | 122 +++++++++++++++++
 .../dialogs/ControlLoop/GetToscaTemplate.js        |  50 +++++++
 .../dialogs/ControlLoop/GetToscaTemplate.test.js   |  53 ++++++++
 .../dialogs/ControlLoop/ReadAndConvertYaml.js      |  96 +++++++++++++
 .../dialogs/ControlLoop/ReadAndConvertYaml.test.js |  99 ++++++++++++++
 .../dialogs/ControlLoop/UploadToscaFile.js         |  54 ++++++++
 .../dialogs/ControlLoop/UploadToscaFile.test.js    |  61 +++++++++
 .../GetLocalToscaFileForUpload.test.js.snap        | 103 ++++++++++++++
 .../__snapshots__/GetToscaTemplate.test.js.snap    |  15 +++
 .../__snapshots__/ReadAndConvertYaml.test.js.snap  |  46 +++++++
 .../__snapshots__/UploadToscaFile.test.js.snap     |  16 +++
 12 files changed, 865 insertions(+)
 create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetLocalToscaFileForUpload.js
 create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetLocalToscaFileForUpload.test.js
 create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.js
 create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.test.js
 create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/ReadAndConvertYaml.js
 create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/ReadAndConvertYaml.test.js
 create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaFile.js
 create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaFile.test.js
 create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/GetLocalToscaFileForUpload.test.js.snap
 create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/GetToscaTemplate.test.js.snap
 create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/ReadAndConvertYaml.test.js.snap
 create mode 100644 gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/UploadToscaFile.test.js.snap

(limited to 'gui-clamp/ui-react/src/components/dialogs/ControlLoop')

diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetLocalToscaFileForUpload.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetLocalToscaFileForUpload.js
new file mode 100644
index 0000000..013dd89
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetLocalToscaFileForUpload.js
@@ -0,0 +1,150 @@
+/*
+ * ============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 Button from "react-bootstrap/Button";
+import Modal from 'react-bootstrap/Modal';
+import Form from 'react-bootstrap/Form';
+import Row from 'react-bootstrap/Row';
+import styled from 'styled-components';
+import Alert from 'react-bootstrap/Alert';
+import * as yaml from "js-yaml";
+import UploadToscaFile from "./UploadToscaFile";
+
+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 GetLocalToscaFileForUpload = (props) => {
+  const [selectedFile, setSelectedFile] = useState();
+  const [fileIsSelected, setFileIsSelected] = useState(false);
+  const [toscaJsonObject, setToscaJsonObject] = useState({});
+  const [show, setShow] = useState(true);
+  const [alertMessages, setAlertMessages] = useState();
+
+  const handleClose = () => {
+    console.log('handleClose called');
+    setShow(false);
+    props.history.push('/');
+  }
+
+  const fileChangeHandler = (event) => {
+    event.preventDefault();
+    console.log('fileChangeHandler called');
+
+    if (event.currentTarget.files[0] !== undefined) {
+      console.log('file defined');
+      setSelectedFile(event.currentTarget.files[0]);
+      setFileIsSelected(true);
+
+      const file = event.currentTarget.files[0];
+
+      setAlertMessages([]);
+
+      const fileReader = new FileReader();
+
+      fileReader.onload = () => {
+        const jsonObj = yaml.load(fileReader.result, 'utf8');
+        setToscaJsonObject(jsonObj);
+      }
+
+      fileReader.readAsText(file);
+
+    } else {
+      return;
+    }
+  };
+
+  const receiveResponseFromUpload = async (response) => {
+
+    if (await response.ok) {
+      setAlertMessages(<Alert variant="success">
+        <Alert.Heading>Upload Success</Alert.Heading>
+        <p>Tosca Service Template 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 Service Template 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>);
+    }
+  };
+
+  return (
+    <ModalStyled size="lg"
+                 show={ show }
+                 onHide={ handleClose }
+                 backdrop="static"
+                 keyboard={ false }>
+      <Modal.Header closeButton>
+        <Modal.Title>Upload Tosca to Commissioning API</Modal.Title>
+      </Modal.Header>
+      <br/>
+      <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={ fileChangeHandler }
+                custom
+                accept=".yaml,.yml,.json"
+                label={ fileIsSelected ? selectedFile.name : 'Please select a file' }
+              >
+              </Form.File>
+              <Form.Text>Only .yaml, .yml and .json files are supported</Form.Text>
+            </Form.Group>
+            <Form.Group as={ Row }>
+              <UploadToscaFile toscaObject={ toscaJsonObject }
+                               onResponseReceived={ receiveResponseFromUpload }/>
+            </Form.Group>
+            <Form.Group as={ Row }>
+              <StyledMessagesDiv>
+                { alertMessages }
+              </StyledMessagesDiv>
+            </Form.Group>
+          </Form>
+        </Modal.Body>
+      </div>
+      <Modal.Footer>
+        <Button variant="secondary"
+                onClick={ handleClose }>Close</Button>
+      </Modal.Footer>
+    </ModalStyled>
+  );
+}
+
+export default GetLocalToscaFileForUpload;
diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetLocalToscaFileForUpload.test.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetLocalToscaFileForUpload.test.js
new file mode 100644
index 0000000..9b885c6
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetLocalToscaFileForUpload.test.js
@@ -0,0 +1,122 @@
+/*
+ * ============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 { mount, shallow } from 'enzyme';
+import toJson from 'enzyme-to-json';
+import { act } from "react-dom/test-utils";
+import GetLocalToscaFileForUpload from './GetLocalToscaFileForUpload';
+import { createMemoryHistory } from 'history';
+
+
+describe('Verify GetLocalToscaFileForUpload', () => {
+  const fs = require('fs');
+  let testFile = fs.readFileSync('src/components/dialogs/Policy/toscaData.test.json');
+  const file = new Blob([testFile], { type: 'file' });
+
+  beforeEach(() => {
+    fetch.resetMocks();
+    fetch.mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+          return Promise.resolve({
+            "tosca_definitions_version": "tosca_simple_yaml_1_1_0",
+            "data_types": {},
+            "policy_types": {},
+            "topology_template": {},
+            "name": "ToscaServiceTemplateSimple",
+            "version": "1.0.0",
+            "metadata": {},
+            "id": "0.19518677404255147"
+          });
+        }
+      });
+    });
+  })
+
+  it("renders without crashing", () => {
+    shallow(<GetLocalToscaFileForUpload/>);
+  });
+
+  it("renders correctly", () => {
+    const tree = shallow(<GetLocalToscaFileForUpload/>);
+    expect(toJson(tree)).toMatchSnapshot();
+  });
+
+  it('should have a UploadToscaFile element', () => {
+    const container = shallow(<GetLocalToscaFileForUpload/>)
+    expect(container.find('UploadToscaFile').length).toEqual(1);
+  });
+
+  it('handleClose called when bottom button clicked', () => {
+    const history = createMemoryHistory();
+    const component = mount(<GetLocalToscaFileForUpload 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(<GetLocalToscaFileForUpload history={ history }/>)
+    const logSpy = jest.spyOn(console, 'log');
+
+
+    act(() => {
+      component.find('[size="lg"]').get(0).props.onHide();
+      expect(logSpy).toHaveBeenCalledWith('handleClose called');
+    });
+  });
+
+  it('should call fileChangeHandler on change, file undefined', () => {
+    const component = mount(<GetLocalToscaFileForUpload/>);
+    const logSpy = jest.spyOn(console, 'log');
+    const event = {
+      preventDefault() {
+      },
+      currentTarget: { files: [] }
+    };
+
+    act(() => {
+      component.find('[type="file"]').get(0).props.onChange(event);
+      expect(logSpy).toHaveBeenCalledWith('fileChangeHandler called');
+    });
+  });
+
+  it('should call fileChangeHandler on change, file defined', async () => {
+    const component = mount(<GetLocalToscaFileForUpload/>);
+    const logSpy = jest.spyOn(console, 'log');
+    const event = {
+      preventDefault() {
+      },
+      currentTarget: { files: [file] }
+    };
+
+    act(async () => {
+      component.find('[type="file"]').get(0).props.onChange(event);
+      expect(logSpy).toHaveBeenCalledWith('file defined');
+    });
+  });
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.js
new file mode 100644
index 0000000..7da8c13
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.js
@@ -0,0 +1,50 @@
+/*
+ * ============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 Button from "react-bootstrap/Button";
+import ControlLoopService from "../../../api/ControlLoopService";
+
+const GetToscaTemplate = (props) => {
+
+  const [windowLocationPathName, setWindowLocationPathname] = useState('');
+
+  const getTemplateHandler = async () => {
+    console.log('getTemplateHandler called')
+    setWindowLocationPathname(window.location.pathname);
+
+    const response = await ControlLoopService.getToscaTemplate(props.templateName, props.templateVersion, windowLocationPathName)
+      .catch(error => error.message);
+
+    props.onGetToscaServiceTemplate(response);
+
+  }
+
+  return (
+    <React.Fragment>
+      <Button variant="primary"
+              type="submit"
+              onClick={ getTemplateHandler }>Get Tosca Service Template</Button>
+    </React.Fragment>
+  );
+
+
+}
+
+export default GetToscaTemplate;
diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.test.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.test.js
new file mode 100644
index 0000000..ce2a398
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/GetToscaTemplate.test.js
@@ -0,0 +1,53 @@
+/*
+ * ============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 { mount, shallow } from 'enzyme';
+import GetToscaTemplate from './GetToscaTemplate';
+import toJson from "enzyme-to-json";
+
+describe('Verify GetToscaTemplate', () => {
+
+  it("renders without crashing", () => {
+    shallow(<GetToscaTemplate/>);
+  });
+
+  it("renders correctly", () => {
+    const tree = shallow(<GetToscaTemplate/>);
+    expect(toJson(tree)).toMatchSnapshot();
+  });
+
+  it('should have a Button element', () => {
+    const container = shallow(<GetToscaTemplate/>)
+    expect(container.find('Button').length).toEqual(1);
+  });
+
+  it('button should call getTemplateHandler when clicked', async () => {
+    const component = mount(<GetToscaTemplate/>)
+    const logSpy = jest.spyOn(console, 'log');
+
+    component.find('[variant="primary"]').simulate('click');
+    expect(logSpy).toHaveBeenCalledWith('getTemplateHandler called');
+  });
+
+  it('should have a Button element with specified text', () => {
+    const container = shallow(<GetToscaTemplate/>)
+    expect(container.find('Button').text()).toBe('Get Tosca Service Template');
+  });
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ReadAndConvertYaml.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ReadAndConvertYaml.js
new file mode 100644
index 0000000..53b541c
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ReadAndConvertYaml.js
@@ -0,0 +1,96 @@
+/*
+ * ============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 GetToscaTemplate from "./GetToscaTemplate";
+import Modal from "react-bootstrap/Modal";
+import Button from "react-bootstrap/Button";
+import { Alert } from "react-bootstrap";
+
+import styled from 'styled-components';
+
+const ModalStyled = styled(Modal)`
+  background-color: transparent;
+`
+
+const AlertStyled = styled(Alert)`
+  margin-top: 10px;
+`
+
+const PreStyled = styled.pre`
+  color: #7F0055;
+  overflow: auto;
+  max-height: 70vh;
+  margin-top: 10px;
+`
+
+const ReadAndConvertYaml = (props) => {
+  const [show, setShow] = useState(true);
+  const [toscaTemplateData, setToscaTemplateData] = useState();
+  const [responeOk, setResponseOk] = useState(true);
+  const name = 'ToscaServiceTemplateSimple';
+  const version = '1.0.0';
+
+  const handleClose = () => {
+    console.log('handleClose called');
+    setShow(false);
+    props.history.push('/');
+  }
+
+  const getToscaServiceTemplateHandler = async (toscaServiceTemplateResponse) => {
+    // console.log('getToscaServiceTemplateHandler called: ' + toscaServiceTemplate);
+
+    if (!toscaServiceTemplateResponse.ok) {
+      setResponseOk(false);
+      const toscaData = await toscaServiceTemplateResponse;
+      setToscaTemplateData(toscaData);
+    } else {
+      setResponseOk(true);
+      const toscaData = await toscaServiceTemplateResponse.json();
+      setToscaTemplateData(toscaData);
+    }
+  }
+
+  return (
+    <ModalStyled size="xl"
+                 show={ show }
+                 onHide={ handleClose }
+                 backdrop="static"
+                 keyboard={ false }>
+      <Modal.Header closeButton>
+        <Modal.Title>View Tosca Template</Modal.Title>
+      </Modal.Header>
+      <Modal.Body>
+        <GetToscaTemplate templateName={ name }
+                          templateVersion={ version }
+                          onGetToscaServiceTemplate={ getToscaServiceTemplateHandler }/>
+        { responeOk && <PreStyled> { JSON.stringify(toscaTemplateData, null, 2) } </PreStyled> }
+        <AlertStyled show={ !responeOk }
+               variant="danger">{ toscaTemplateData }</AlertStyled>
+      </Modal.Body>
+      <Modal.Footer>
+        <Button variant="secondary"
+                type="null"
+                onClick={ handleClose }>Cancel</Button>
+      </Modal.Footer>
+    </ModalStyled>
+  );
+};
+
+export default ReadAndConvertYaml;
diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ReadAndConvertYaml.test.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ReadAndConvertYaml.test.js
new file mode 100644
index 0000000..90d7185
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/ReadAndConvertYaml.test.js
@@ -0,0 +1,99 @@
+/*
+ * ============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 { mount, shallow } from 'enzyme';
+import ReadAndConvertYaml from './ReadAndConvertYaml';
+import toJson from "enzyme-to-json";
+import { act } from "react-dom/test-utils";
+import { createMemoryHistory } from "history";
+
+describe('Verify ReadAndConvertYaml', () => {
+
+  beforeEach(() => {
+    fetch.resetMocks();
+    fetch.mockImplementation(() => {
+      return Promise.resolve({
+        ok: true,
+        status: 200,
+        json: () => {
+          return Promise.resolve({
+            "tosca_definitions_version": "tosca_simple_yaml_1_1_0",
+            "data_types": {},
+            "policy_types": {},
+            "topology_template": {},
+            "name": "ToscaServiceTemplateSimple",
+            "version": "1.0.0",
+            "metadata": {},
+            "id": "0.19518677404255147"
+          });
+        }
+      });
+    });
+  })
+
+  it("renders without crashing", () => {
+    shallow(<ReadAndConvertYaml/>);
+  });
+
+  it("renders correctly", () => {
+    const tree = shallow(<ReadAndConvertYaml/>);
+    expect(toJson(tree)).toMatchSnapshot();
+  });
+
+  it('should have a GetToscaTemplate element', () => {
+    const container = shallow(<ReadAndConvertYaml/>)
+    expect(container.find('GetToscaTemplate').length).toEqual(1);
+  });
+
+  it('should call getToscaServiceTemplateHandler on click', async () => {
+    const component = mount(<ReadAndConvertYaml/>);
+    const logSpy = jest.spyOn(console, 'log');
+
+    act(async () => {
+      component.find('GetToscaTemplate').simulate('click');
+      expect(logSpy).toHaveBeenCalledWith('getToscaServiceTemplateHandler called');
+    });
+  });
+
+  it('handleClose called when bottom button clicked', () => {
+    const history = createMemoryHistory();
+    const component = mount(<ReadAndConvertYaml 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(<ReadAndConvertYaml 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/UploadToscaFile.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaFile.js
new file mode 100644
index 0000000..6ee6a43
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaFile.js
@@ -0,0 +1,54 @@
+/*
+ * ============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 Button from "react-bootstrap/Button";
+import React, { useState } from "react";
+import ControlLoopService from "../../../api/ControlLoopService";
+
+const UploadToscaFile = (props) => {
+  const [windowLocationPathName, setWindowLocationPathname] = useState('');
+
+  const postServiceTemplateHandler = async (event) => {
+    event.preventDefault();
+    console.log('postServiceTemplateHandler called');
+    setWindowLocationPathname(window.location.pathname);
+
+    const response = await ControlLoopService.uploadToscaFile(props.toscaObject, windowLocationPathName)
+      .catch(error => error.message);
+
+    // const responseMessage = await response.text();
+
+    props.onResponseReceived(response);
+
+  }
+
+  return (
+    <React.Fragment>
+      <Button variant="primary"
+              block={ true }
+              type="submit"
+              onClick={ postServiceTemplateHandler }>
+        Upload Tosca Service Template
+      </Button>
+    </React.Fragment>
+  );
+
+};
+
+export default UploadToscaFile;
diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaFile.test.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaFile.test.js
new file mode 100644
index 0000000..94d9038
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/UploadToscaFile.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 React from 'react';
+import { mount, shallow } from 'enzyme';
+import UploadToscaFile from './UploadToscaFile';
+import toJson from "enzyme-to-json";
+import { act } from "react-dom/test-utils";
+
+describe('Verify UploadToscaFile', () => {
+
+  it("renders without crashing", () => {
+    shallow(<UploadToscaFile/>);
+  });
+
+  it("renders correctly", () => {
+    const tree = shallow(<UploadToscaFile/>);
+    expect(toJson(tree)).toMatchSnapshot();
+  });
+
+  it('should have a Button element', () => {
+    const container = shallow(<UploadToscaFile/>)
+    expect(container.find('Button').length).toEqual(1);
+  });
+
+  it('button should call postServiceTemplateHandler when clicked', async () => {
+    const component = mount(<UploadToscaFile/>)
+    const logSpy = jest.spyOn(console, 'log');
+    const event = {
+      preventDefault() {
+      }
+    };
+
+    act(async () => {
+      component.find('[variant="primary"]').get(0).props.onClick(event);
+      expect(logSpy).toHaveBeenCalledWith('postServiceTemplateHandler called');
+    })
+
+  });
+
+  it('should have a Button element with specified text', () => {
+    const container = shallow(<UploadToscaFile/>)
+    expect(container.find('Button').text()).toBe('Upload Tosca Service Template');
+  });
+});
diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/GetLocalToscaFileForUpload.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/GetLocalToscaFileForUpload.test.js.snap
new file mode 100644
index 0000000..84168fd
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/GetLocalToscaFileForUpload.test.js.snap
@@ -0,0 +1,103 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify GetLocalToscaFileForUpload renders correctly 1`] = `
+<Styled(Modal)
+  backdrop="static"
+  keyboard={false}
+  onHide={[Function]}
+  show={true}
+  size="lg"
+>
+  <ModalHeader
+    closeButton={true}
+    closeLabel="Close"
+  >
+    <ModalTitle>
+      Upload Tosca to Commissioning API
+    </ModalTitle>
+  </ModalHeader>
+  <br />
+  <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"
+          />
+          <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],
+            }
+          }
+        >
+          <UploadToscaFile
+            onResponseReceived={[Function]}
+            toscaObject={Object {}}
+          />
+        </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]}
+      variant="secondary"
+    >
+      Close
+    </Button>
+  </ModalFooter>
+</Styled(Modal)>
+`;
diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/GetToscaTemplate.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/GetToscaTemplate.test.js.snap
new file mode 100644
index 0000000..2c591d9
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/GetToscaTemplate.test.js.snap
@@ -0,0 +1,15 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify GetToscaTemplate renders correctly 1`] = `
+<Fragment>
+  <Button
+    active={false}
+    disabled={false}
+    onClick={[Function]}
+    type="submit"
+    variant="primary"
+  >
+    Get Tosca Service Template
+  </Button>
+</Fragment>
+`;
diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/ReadAndConvertYaml.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/ReadAndConvertYaml.test.js.snap
new file mode 100644
index 0000000..fab7801
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/ReadAndConvertYaml.test.js.snap
@@ -0,0 +1,46 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify ReadAndConvertYaml renders correctly 1`] = `
+<Styled(Modal)
+  backdrop="static"
+  keyboard={false}
+  onHide={[Function]}
+  show={true}
+  size="xl"
+>
+  <ModalHeader
+    closeButton={true}
+    closeLabel="Close"
+  >
+    <ModalTitle>
+      View Tosca Template
+    </ModalTitle>
+  </ModalHeader>
+  <ModalBody>
+    <GetToscaTemplate
+      onGetToscaServiceTemplate={[Function]}
+      templateName="ToscaServiceTemplateSimple"
+      templateVersion="1.0.0"
+    />
+    <styled.pre>
+       
+       
+    </styled.pre>
+    <Styled(Alert)
+      show={false}
+      variant="danger"
+    />
+  </ModalBody>
+  <ModalFooter>
+    <Button
+      active={false}
+      disabled={false}
+      onClick={[Function]}
+      type="null"
+      variant="secondary"
+    >
+      Cancel
+    </Button>
+  </ModalFooter>
+</Styled(Modal)>
+`;
diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/UploadToscaFile.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/UploadToscaFile.test.js.snap
new file mode 100644
index 0000000..a1ae439
--- /dev/null
+++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/UploadToscaFile.test.js.snap
@@ -0,0 +1,16 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`Verify UploadToscaFile renders correctly 1`] = `
+<Fragment>
+  <Button
+    active={false}
+    block={true}
+    disabled={false}
+    onClick={[Function]}
+    type="submit"
+    variant="primary"
+  >
+    Upload Tosca Service Template
+  </Button>
+</Fragment>
+`;
-- 
cgit