From 83c09e788eb75b287c946acbcba0d43cd3cd4681 Mon Sep 17 00:00:00 2001 From: "saul.gill" Date: Mon, 26 Jul 2021 17:33:24 +0100 Subject: Added Common Parameter Form Added save button for user edits Added commission button to push up edits Added service to retrieve common parameters Issue-ID: POLICY-3439 Change-Id: Ia355f36bf263bb1385db363a267c53af86504905 Signed-off-by: saul.gill --- .../dialogs/ControlLoop/CommissioningModal.js | 192 +++++++++++++++++---- .../dialogs/ControlLoop/CommissioningModal.test.js | 4 +- .../__snapshots__/CommissioningModal.test.js.snap | 14 +- 3 files changed, 175 insertions(+), 35 deletions(-) (limited to 'gui-clamp/ui-react/src/components/dialogs') diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.js index 6baa06c..ffe149a 100644 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.js +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.js @@ -23,7 +23,7 @@ import React, { useEffect, useState } from "react"; import styled from "styled-components"; import { JSONEditor } from "@json-editor/json-editor"; import ControlLoopService from "../../../api/ControlLoopService"; -import OnapConstant from "../../../utils/OnapConstants"; +import { Alert } from "react-bootstrap"; const ModalStyled = styled(Modal)` @media (min-width: 800px) { @@ -44,10 +44,26 @@ const StyledMessagesDiv = styled.div` const CommissioningModal = (props) => { const [windowLocationPathName, setWindowLocationPathName] = useState(''); + const [fullToscaTemplate, setFullToscaTemplate] = useState({}); const [toscaInitialValues, setToscaInitialValues] = useState({}); + const [commonProperties, setCommonProperties] = useState({}) const [toscaJsonSchema, setToscaJsonSchema] = useState({}); - const [jsonEditor, setJsonEditor] = useState({}); + const [jsonEditor, setJsonEditor] = useState(null); const [show, setShow] = useState(true); + const [alertMessages, setAlertMessages] = useState(); + const name = 'ToscaServiceTemplateSimple'; + const version = '1.0.0'; + let editorTemp = null + + useEffect(async () => { + const toscaTemplateResponse = await ControlLoopService.getToscaTemplate(name, version, windowLocationPathName) + .catch(error => error.message); + const toscaCommonProperties = await ControlLoopService.getCommonProperties(name, version, windowLocationPathName) + .catch(error => error.message); + + await renderJsonEditor(toscaTemplateResponse, toscaCommonProperties) + + }, []); const handleClose = () => { console.log('handleClose called'); @@ -55,43 +71,150 @@ const CommissioningModal = (props) => { props.history.push('/'); } - const getToscaServiceTemplateHandler = async (toscaServiceTemplateResponse) => { + const handleSave = () => { + updateTemplate(jsonEditor.getValue()) + } - if (!toscaServiceTemplateResponse.ok) { - const toscaData = await toscaServiceTemplateResponse; - setToscaInitialValues(toscaData); - } else { - const toscaData = await toscaServiceTemplateResponse.json(); - setToscaInitialValues(toscaData); - } + + + const handleCommission = async () => { + setWindowLocationPathName(window.location.pathname); + + await ControlLoopService.deleteToscaTemplate('ToscaServiceTemplateSimple', "1.0.0", windowLocationPathName) + .catch(error => error.message) + + const recommissioningResponse = await ControlLoopService.uploadToscaFile(fullToscaTemplate, windowLocationPathName) + .catch(error => error.message) + + await receiveResponseFromCommissioning(recommissioningResponse) } - const getToscaSchemaHandler = async (toscaSchemaResponse) => { + const receiveResponseFromCommissioning = async (response) => { - if (!toscaSchemaResponse.ok) { - const toscaSchemaData = await toscaSchemaResponse; - setToscaJsonSchema(toscaSchemaData); - } else { - const toscaSchemaData = await toscaSchemaResponse.json(); - setToscaJsonSchema(toscaSchemaData); + if (await response.ok) { + setAlertMessages( + Commissioning Success +

Altered Template was Successfully Uploaded

+
+
); + } + else { + setAlertMessages( + Commissioning Failure +

Updated Template Failed to Upload

+

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

+

Response Text: { await response.text() }

+
+
); } + }; + + const renderJsonEditor = async (template, commonProps) => { + + const fullTemplate = await template.json() + + setFullToscaTemplate(fullTemplate) + const allNodeTemplates = fullTemplate.topology_template.node_templates + const shortenedNodeTemplatesObjectStartValues = {} + // Get the common properties to construct a schema + // Get valid start values at the same time + const commonNodeTemplatesJson = await commonProps.json().then(data => { + const nodeTemplatesArray = Object.entries(data) + const shortenedNodeTemplatesObject = {} + nodeTemplatesArray.forEach(([key, temp]) => { + const currentNodeTemplate = allNodeTemplates[key] + const propertiesObject = { + properties: temp.properties + } + + shortenedNodeTemplatesObject[key] = propertiesObject + + const propertiesStartValues = {} + + // Get all the existing start values to populate the properties in the schema + Object.entries(propertiesObject.properties).forEach(([propKey, prop]) => { + propertiesStartValues[propKey] = currentNodeTemplate.properties[propKey] + }) + + shortenedNodeTemplatesObjectStartValues[key] = propertiesStartValues + + }) + + setToscaInitialValues(shortenedNodeTemplatesObjectStartValues) + return shortenedNodeTemplatesObject; + }) + + const propertySchema = makeSchemaForCommonProperties(commonNodeTemplatesJson) + setToscaJsonSchema(propertySchema) + + editorTemp = createJsonEditor(propertySchema, shortenedNodeTemplatesObjectStartValues); + setJsonEditor(editorTemp) } - useEffect(async () => { - setWindowLocationPathName(window.location.pathname); + const updateTemplate = (userAddedCommonPropValues) => { + const nodeTemplates = fullToscaTemplate.topology_template.node_templates + const commonPropertyDataEntries = Object.entries(userAddedCommonPropValues) - const toscaTemplateResponse = await ControlLoopService.getToscaControlLoopDefinitions(windowLocationPathName) - .catch(error => error.message); + // This replaces the values for properties in the full tosca template + // that will be sent up to the api with the entries the user provided. + commonPropertyDataEntries.forEach(([templateKey, values]) => { + Object.entries(values).forEach(([propKey, propVal]) => { + nodeTemplates[templateKey].properties[propKey] = propVal + }) + }) + + fullToscaTemplate.topology_template.node_templates = nodeTemplates + + setFullToscaTemplate(fullToscaTemplate) + alert('Changes saved. Commission When Ready...') + } - const toscaSchemaResponse = await ControlLoopService.getToscaServiceTemplateSchema('node_templates', windowLocationPathName); + const makeSchemaForCommonProperties = (commonProps) => { + const commonPropsArr = Object.entries(commonProps) - createJsonEditor(await toscaSchemaResponse.json(), await toscaTemplateResponse.json()); + const newSchemaObject = {} - }, []); + newSchemaObject.title = "CommonProperties" + newSchemaObject.type = "object" + newSchemaObject.properties = {} + + commonPropsArr.forEach(([templateKey, template]) => { + + const propertiesObject = {} + Object.entries(template.properties).forEach(([propKey, prop]) => { + + propertiesObject[propKey] = { + type: getType(prop.type) + } + + }) + newSchemaObject.properties[templateKey] = { + properties: propertiesObject + } + }) + + return newSchemaObject + + } + + const getType = (propertyType) => { + switch (propertyType) + { + case "string": + return "string" + case "integer": + return "integer" + case "list": + return "array" + case "object": + return "object" + default: + return "string" + } + } - // TODO Need to Hook this up to a new camel endpoint to get it working const createJsonEditor = (toscaModel, editorData) => { - JSONEditor.defaults.options.collapse = true; + JSONEditor.defaults.options.collapse = false; return new JSONEditor(document.getElementById("editor"), { @@ -101,13 +224,13 @@ const CommissioningModal = (props) => { iconlib: 'fontawesome5', object_layout: 'normal', disable_properties: false, - disable_edit_json: false, + disable_edit_json: true, disable_array_reorder: true, disable_array_delete_last_row: true, disable_array_delete_all_rows: false, array_controls_top: true, keep_oneof_values: false, - collapsed: true, + collapsed: false, show_errors: 'always', display_required_only: false, show_opt_in: false, @@ -123,7 +246,7 @@ const CommissioningModal = (props) => { backdrop="static" keyboard={ false }> - Edit Control Loop Parameters + Edit Common Properties
@@ -131,9 +254,16 @@ const CommissioningModal = (props) => {
+ + { alertMessages } + - + + diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.test.js b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.test.js index f189c58..054450c 100644 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.test.js +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/CommissioningModal.test.js @@ -37,9 +37,9 @@ describe('Verify ReadAndConvertYaml', () => { expect(toJson(tree)).toMatchSnapshot(); }); - it('should have two Button elements', () => { + it('should have three Button elements', () => { const container = shallow() - expect(container.find('Button').length).toEqual(2); + expect(container.find('Button').length).toEqual(3); }); it('handleClose called when bottom button clicked', () => { diff --git a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/CommissioningModal.test.js.snap b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/CommissioningModal.test.js.snap index 78c8983..41c13de 100644 --- a/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/CommissioningModal.test.js.snap +++ b/gui-clamp/ui-react/src/components/dialogs/ControlLoop/__snapshots__/CommissioningModal.test.js.snap @@ -13,7 +13,7 @@ exports[`Verify ReadAndConvertYaml renders correctly 1`] = ` closeLabel="Close" > - Edit Control Loop Parameters + Edit Common Properties
@@ -30,13 +30,23 @@ exports[`Verify ReadAndConvertYaml renders correctly 1`] = ` />
+ +