From 280f8015d06af1f41a3ef12e8300801c7a5e0d54 Mon Sep 17 00:00:00 2001 From: AviZi Date: Fri, 9 Jun 2017 02:39:56 +0300 Subject: [SDC-29] Amdocs OnBoard 1707 initial commit. Change-Id: Ie4d12a3f574008b792899b368a0902a8b46b5370 Signed-off-by: AviZi --- .../attachments/validation/HeatValidation.js | 51 ++++ .../validation/HeatValidationActionHelper.js | 39 +++ .../validation/HeatValidationConstants.js | 57 +++++ .../validation/HeatValidationReducer.js | 196 +++++++++++++++ .../attachments/validation/HeatValidationView.jsx | 274 +++++++++++++++++++++ 5 files changed, 617 insertions(+) create mode 100644 openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js create mode 100644 openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationActionHelper.js create mode 100644 openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js create mode 100644 openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationReducer.js create mode 100644 openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx (limited to 'openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation') diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js new file mode 100644 index 0000000000..21f6e6c77f --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidation.js @@ -0,0 +1,51 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * 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. + */ +import {connect} from 'react-redux'; +import HeatValidationView from './HeatValidationView.jsx'; +import HeatValidationActionHelper from './HeatValidationActionHelper.js'; +import {errorLevels, nodeFilters} from './HeatValidationConstants.js'; + +export const mapStateToProps = ({softwareProduct: {softwareProductAttachments: {heatValidation}}}) => { + let {attachmentsTree, selectedNode, errorList} = heatValidation; + let currentErrors = [], currentWarnings = []; + if (errorList) { + for (let i = 0 ; i < errorList.length ; i++) { + if (errorList[i].level === errorLevels.ERROR && (errorList[i].name === selectedNode || selectedNode === nodeFilters.ALL)) { + currentErrors[currentErrors.length] = errorList[i]; + } + if (errorList[i].level === errorLevels.WARNING && (errorList[i].name === selectedNode || selectedNode === nodeFilters.ALL)) { + currentWarnings[currentWarnings.length] = errorList[i]; + } + } + } + return { + attachmentsTree, + selectedNode, + errorList, + currentErrors, + currentWarnings + }; +}; + +const mapActionsToProps = (dispatch) => { + return { + toggleExpanded: (path) => HeatValidationActionHelper.toggleExpanded(dispatch, {path}), + onSelectNode: (nodeName) => HeatValidationActionHelper.onSelectNode(dispatch, {nodeName}), + onDeselectNode: () => HeatValidationActionHelper.onDeselectNode(dispatch) + }; +}; + +export default connect(mapStateToProps, mapActionsToProps, null, {withRef: true})(HeatValidationView); diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationActionHelper.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationActionHelper.js new file mode 100644 index 0000000000..73366c20cc --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationActionHelper.js @@ -0,0 +1,39 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * 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. + */ +import {actionTypes} from './HeatValidationConstants.js'; + +export default { + + toggleExpanded(dispatch, {path}) { + dispatch({ + type: actionTypes.TOGGLE_EXPANDED, + path + }); + }, + + onSelectNode(dispatch, {nodeName}) { + dispatch({ + type: actionTypes.SELECTED_NODE, + nodeName + }); + }, + + onDeselectNode(dispatch) { + dispatch({ + type: actionTypes.UNSELECTED_NODE + }); + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js new file mode 100644 index 0000000000..f783fe6482 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationConstants.js @@ -0,0 +1,57 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * 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. + */ +import keyMirror from 'nfvo-utils/KeyMirror.js'; +import i18n from 'nfvo-utils/i18n/i18n.js'; + +export const actionTypes = keyMirror({ + TOGGLE_EXPANDED: null, + SELECTED_NODE: null, + UNSELECTED_NODE: null +}); + +export const errorTypes = keyMirror({ + MISSING_FILE_IN_ZIP: i18n('missing file in zip'), + MISSING_FILE_IN_MANIFEST: i18n('missing file in manifest'), + MISSING_OR_ILLEGAL_FILE_TYPE_IN_MANIFEST: i18n('missing or illegal file type in manifest'), + FILE_IS_YML_WITHOUT_YML_EXTENSION: i18n('file is defined as a heat file but it doesn\'t have .yml or .yaml extension'), + FILE_IS_ENV_WITHOUT_ENV_EXTENSION: i18n('file is defined as an env file but it doesn\'t have .env extension'), + ILLEGAL_YAML_FILE_CONTENT: i18n('illegal yaml file content'), + ILLEGAL_HEAT_YAML_FILE_CONTENT: i18n('illegal HEAT yaml file content'), + MISSING_FILE_NAME_IN_MANIFEST: i18n('a file is written in manifest without file name'), + MISSING_ENV_FILE_IN_ZIP: i18n('missing env file in zip'), + ARTIFACT_NOT_IN_USE: i18n('artifact not in use') +}); + +export const errorLevels = keyMirror({ + WARNING: 'WARNING', + ERROR: 'ERROR' +}); +export const nodeFilters = keyMirror({ + ALL: 'All' +}); +export const nodeTypes = keyMirror({ + heat: i18n('Heat'), + volume: i18n('Volume'), + network: i18n('Network'), + artifact: i18n('Artifact'), + env: i18n('Environment'), + other: i18n('') +}); + +export const mouseActions = keyMirror({ + MOUSE_BUTTON_CLICK: 0 +}); + diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationReducer.js b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationReducer.js new file mode 100644 index 0000000000..f0c10ed457 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationReducer.js @@ -0,0 +1,196 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * 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. + */ +import {actionTypes as softwareProductsActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js'; +import {actionTypes, nodeFilters} from './HeatValidationConstants.js'; + +const mapVolumeData = ({fileName, env, errors}) => ({ + name: fileName, + expanded: true, + type: 'volume', + children: env && [{ + name: env.fileName, + errors: env.errors, + type: 'env' + }], + errors +}); + +const mapNetworkData = ({fileName, env, errors}) => ({ + name: fileName, + expanded: true, + type: 'network', + children: env && [{ + name: env.fileName, + errors: env.errors, + type: 'env' + }], + errors +}); + +const mapArtifactsData = ({fileName, errors}) => ({ + name: fileName, + type: 'artifact', + errors +}); + +const mapOtherData = ({fileName, errors}) => ({ + name: fileName, + type: 'other', + errors +}); + + +const mapHeatData = ({fileName, env, nested, volume, network, artifacts, errors, other}) => ({ + name: fileName, + expanded: true, + type: 'heat', + errors, + children: [ + ...(volume ? volume.map(mapVolumeData) : []), + ...(network ? network.map(mapNetworkData) : []), + ...(env ? [{ + name: env.fileName, + errors: env.errors, + type: 'env' + }] : []), + ...(artifacts ? artifacts.map(mapArtifactsData) : []), + ...(other ? other.map(mapOtherData) : []), + ...(nested ? nested.map(mapHeatData) : []) + ] +}); + +function createErrorList(node, parent, deep = 0, errorList = []) { + if (node.errors) { + errorList.push(...node.errors.map((error) => ({ + level: error.level, + errorMessage: error.message, + name: node.name, + hasParent: deep > 2, + parentName: parent.name, + type: node.type, + }))); + } + if (node.children && node.children.length) { + node.children.map((child) => createErrorList(child, node, deep + 1, errorList)); + } + return errorList; +} + +const mapValidationDataToTree = validationData => { + let {heat, volume, network, artifacts, other} = validationData.importStructure || {}; + return { + children: [ + { + name: 'HEAT', + expanded: true, + type: 'heat', + header: true, + children: (heat ? heat.map(mapHeatData) : []) + }, + ...(artifacts ? [{ + name: 'artifacts', + expanded: true, + type: 'artifact', + children: (artifacts ? artifacts.map(mapArtifactsData) : []) + }] : []), + ...(network ? [{ + name: 'networks', + expanded: true, + type: 'network', + children: (network ? network.map(mapNetworkData) : []), + }] : []), + ...(volume ? [{ + name: 'volume', + expanded: true, + type: 'volume', + children: (volume ? volume.map(mapVolumeData) : []), + }] : []), + ...(other ? [{ + name: 'other', + expanded: true, + type: 'other', + children: (other ? other.map(mapOtherData) : []), + }] : []) + ] + }; +}; + +const toggleExpanded = (node, path) => { + let newNode = {...node}; + if (path.length === 0) { + newNode.expanded = !node.expanded; + } else { + let index = path[0]; + newNode.children = [ + ...node.children.slice(0, index), + toggleExpanded(node.children[index], path.slice(1)), + ...node.children.slice(index + 1) + ]; + } + return newNode; +}; + +const expandSelected = (node, selectedNode) => { + let shouldExpand = node.name === selectedNode; + let children = node.children && node.children.map(child => { + let {shouldExpand: shouldExpandChild, node: newChild} = expandSelected(child, selectedNode); + shouldExpand = shouldExpand || shouldExpandChild; + return newChild; + }); + + return { + node: { + ...node, + expanded: node.expanded || shouldExpand, + children + }, + shouldExpand + }; +}; + +export default (state = {attachmentsTree: {}}, action) => { + switch (action.type) { + case softwareProductsActionTypes.SOFTWARE_PRODUCT_LOADED: + let currentSoftwareProduct = action.response; + let attachmentsTree = currentSoftwareProduct.validationData ? mapValidationDataToTree(currentSoftwareProduct.validationData) : {}; + let errorList = createErrorList(attachmentsTree); + return { + ...state, + attachmentsTree, + errorList, + selectedNode: nodeFilters.ALL + }; + case actionTypes.TOGGLE_EXPANDED: + return { + ...state, + attachmentsTree: toggleExpanded(state.attachmentsTree, action.path) + }; + case actionTypes.SELECTED_NODE: + let selectedNode = action.nodeName; + return { + ...state, + attachmentsTree: expandSelected(state.attachmentsTree, selectedNode).node, + selectedNode + }; + case actionTypes.UNSELECTED_NODE: + return { + ...state, + selectedNode: nodeFilters.ALL + }; + default: + return state; + } +}; diff --git a/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx new file mode 100644 index 0000000000..25ad90f351 --- /dev/null +++ b/openecomp-ui/src/sdc-app/onboarding/softwareProduct/attachments/validation/HeatValidationView.jsx @@ -0,0 +1,274 @@ +/*! + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * + * 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. + */ +import React, {Component, PropTypes} from 'react'; +import classNames from 'classnames'; +import Collapse from 'react-bootstrap/lib/Collapse.js'; +import Icon from 'nfvo-components/icon/Icon.jsx'; +import SVGIcon from 'nfvo-components/icon/SVGIcon.jsx'; +import i18n from 'nfvo-utils/i18n/i18n.js'; +import {mouseActions, errorLevels, nodeFilters} from './HeatValidationConstants.js'; + +const leftPanelWidth = 250; +const typeToIcon = Object.freeze({ + heat: 'heat', + volume: 'volume', + network: 'network', + artifact: 'validation-artifacts', + env: 'env', + other: 'validation-other' +}); + + +class HeatValidationView extends Component { + + static propTypes = { + attachmentsTree: PropTypes.object.isRequired, + errorList: PropTypes.array.isRequired, + currentErrors: PropTypes.array.isRequired, + currentWarnings: PropTypes.array.isRequired, + onSelectNode: PropTypes.func.isRequired, + onDeselectNode: PropTypes.func.isRequired, + toggleExpanded: PropTypes.func.isRequired, + selectedNode: PropTypes.string + }; + + render() { + return (
+ + +
); + } +} + +function HeatFileTreeRow(props) { + let {node, path, toggleExpanded, selectedNode, selectNode} = props; + let isFolder = node.children && node.children.length > 0; + return ( +
toggleExpanded(path)} className={classNames({ + 'tree-node-row': true, + 'tree-node-clicked': node.name === props.selectedNode + })} data-test-id='validation-tree-node'> +
+ { + isFolder && +
toggleExpanded(path)} + className='tree-node-expander'> + +
+ } + { + + + + + } + { + + selectNode(node.name)} data-test-id='validation-tree-node-name'> + {node.name ? node.name : 'UNKNOWN'} + + } +
+ selectNode(node.name)} /> +
); +} + +function HeatFileTreeHeader(props) { + let hasErrors = props.errorList.filter(error => error.level === errorLevels.ERROR).length > 0; + return ( +
props.selectNode(nodeFilters.ALL)} className={classNames({'attachments-tree-header': true, + 'header-selected' : props.selectedNode === nodeFilters.ALL})} data-test-id='validation-tree-header'> +
+ + {i18n(`HEAT${hasErrors ? ' (Draft)' : ''}`)} +
+ +
); +} + +class HeatFileTree extends React.Component { + static propTypes = { + attachmentsTree: PropTypes.object.isRequired, + errorList: PropTypes.array.isRequired, + onSelectNode: PropTypes.func.isRequired, + onDeselectNode: PropTypes.func.isRequired, + toggleExpanded: PropTypes.func.isRequired, + selectedNode: PropTypes.string + }; + state = { + treeWidth: '400' + }; + render() { + let {attachmentsTree} = this.props; + return ( +
+
+
+ {attachmentsTree && attachmentsTree.children && attachmentsTree.children.map((child, ind) => this.renderNode(child, [ind]))} +
+
+
this.onChangeTreeWidth(e)} + className='vsp-attachments-heat-validation-separator' data-test-id='validation-tree-separator'>
+
); + } + renderNode(node, path) { + let rand = Math.random() * (3000 - 1) + 1; + let isFolder = node.children && node.children.length > 0; + let {selectedNode} = this.props; + return ( +
+ { + node.header ? + this.selectNode(nodeName)} /> : + this.selectNode(node.name)} /> + } + { + isFolder && + +
+ { + node.children.map((child, ind) => this.renderNode(child, [...path, ind])) + } +
+
+ } +
+ ); + } + + + + + + selectNode(currentSelectedNode) { + let {onDeselectNode, onSelectNode, selectedNode} = this.props; + if (currentSelectedNode !== selectedNode) { + onSelectNode(currentSelectedNode); + } else { + onDeselectNode(); + } + + + + } + + onChangeTreeWidth(e) { + if (e.button === mouseActions.MOUSE_BUTTON_CLICK) { + let onMouseMove = (e) => { + this.setState({treeWidth: e.clientX - leftPanelWidth}); + }; + let onMouseUp = () => { + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + }; + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + } + } +} + +class HeatMessageBoard extends Component { + static propTypes = { + currentErrors: PropTypes.array, + currentWarnings: PropTypes.array, + selectedNode: PropTypes.string + }; + render() { + let {errors, warnings} = this.props; + let allItems = [...errors, ...warnings]; + return ( +
+ { allItems.map(error => this.renderError(error)) } +
+ ); + } + renderError(error) { + let rand = Math.random() * (3000 - 1) + 1; + return ( +
+ {error.level === errorLevels.WARNING ? + : } + + { + (this.props.selectedNode === nodeFilters.ALL) ? + + + {i18n('{errorName}:', { + errorName: error.name + })} + + + {i18n('{message}', {message: error.errorMessage})} + + : + i18n('{errorMsg}', { + errorMsg: error.errorMessage + }) + } + +
+ ); + } +} +class ErrorsAndWarningsCount extends Component { + static propTypes = { + errorList: PropTypes.array, + size: PropTypes.string + }; + render() { + let errors = this.getErrorsAndWarningsCount(this.props.errorList); + if (!errors) { + return null; + } + let errIcon = 'error'; + let {size} = this.props; + if (size && size === 'large') { + errIcon += '-lg'; + } + return (
+ {(errors.errorCount > 0) &&
+ +
{errors.errorCount}
+
} + {(errors.warningCount > 0) &&
+ +
{errors.warningCount}
+
} +
); + } + getErrorsAndWarningsCount(errorList) { + let errorCount = 0, warningCount = 0; + if (errorList && errorList.length > 0) { + for (let i = 0; i < errorList.length; i++) { + if (errorList[i].level === errorLevels.ERROR) { + errorCount++; + } else if (errorList[i].level === errorLevels.WARNING) { + warningCount++; + } + } + } + if (errorCount === 0 && warningCount === 0) { + return null; + } + return {errorCount, warningCount}; + } +} +export default HeatValidationView; -- cgit 1.2.3-korg