summaryrefslogtreecommitdiffstats
path: root/openecomp-ui/src/sdc-app/heatvalidation/attachments
diff options
context:
space:
mode:
Diffstat (limited to 'openecomp-ui/src/sdc-app/heatvalidation/attachments')
-rw-r--r--openecomp-ui/src/sdc-app/heatvalidation/attachments/Attachments.js46
-rw-r--r--openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsActionHelper.js44
-rw-r--r--openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsConstants.js55
-rw-r--r--openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsReducer.js199
-rw-r--r--openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsView.jsx190
5 files changed, 534 insertions, 0 deletions
diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/Attachments.js b/openecomp-ui/src/sdc-app/heatvalidation/attachments/Attachments.js
new file mode 100644
index 0000000000..2a6a992844
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/heatvalidation/attachments/Attachments.js
@@ -0,0 +1,46 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+import {connect} from 'react-redux';
+import AttachmentsView from './AttachmentsView.jsx';
+import AttachmentsActionHelper from './AttachmentsActionHelper.js';
+
+
+const mapStateToProps = ({uploadScreen: {attachments}}) => {
+ let {attachmentsTree = false, hoveredNode, selectedNode, errorList} = attachments;
+ return {
+ attachmentsTree,
+ hoveredNode,
+ selectedNode,
+ errorList
+ };
+};
+
+const mapActionsToProps = (dispatch) => {
+ return {
+
+ toggleExpanded: (path) => AttachmentsActionHelper.toggleExpanded(dispatch, {path}),
+ onSelectNode: (nodeName) => AttachmentsActionHelper.onSelectNode(dispatch, {nodeName}),
+ onUnselectNode: () => AttachmentsActionHelper.onUnselectNode(dispatch),
+
+ };
+};
+
+export default connect(mapStateToProps, mapActionsToProps)(AttachmentsView);
diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsActionHelper.js b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsActionHelper.js
new file mode 100644
index 0000000000..15b0ffa4a9
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsActionHelper.js
@@ -0,0 +1,44 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+import {actionTypes} from './AttachmentsConstants.js';
+
+export default {
+
+ toggleExpanded(dispatch, {path}) {
+ dispatch({
+ type: actionTypes.TOGGLE_EXPANDED,
+ path
+ });
+ },
+
+ onSelectNode(dispatch, {nodeName}) {
+ dispatch({
+ type: actionTypes.SELECTED_NODE,
+ nodeName
+ });
+ },
+
+ onUnselectNode(dispatch) {
+ dispatch({
+ type: actionTypes.UNSELECTED_NODE
+ });
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsConstants.js b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsConstants.js
new file mode 100644
index 0000000000..33af476d9c
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsConstants.js
@@ -0,0 +1,55 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+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 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/heatvalidation/attachments/AttachmentsReducer.js b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsReducer.js
new file mode 100644
index 0000000000..01f68aede8
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsReducer.js
@@ -0,0 +1,199 @@
+/*-
+ * ============LICENSE_START=======================================================
+ * SDC
+ * ================================================================================
+ * 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.
+ * ============LICENSE_END=========================================================
+ */
+
+import {actionTypes as softwareProductsActionTypes} from 'sdc-app/onboarding/softwareProduct/SoftwareProductConstants.js';
+import {actionTypes} from './AttachmentsConstants.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) => ({
+ errorLevel: 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',
+ 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
+ };
+ 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: undefined
+ };
+ default:
+ return state;
+ }
+};
diff --git a/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsView.jsx b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsView.jsx
new file mode 100644
index 0000000000..7e2dda8d47
--- /dev/null
+++ b/openecomp-ui/src/sdc-app/heatvalidation/attachments/AttachmentsView.jsx
@@ -0,0 +1,190 @@
+import React from 'react';
+import FontAwesome from 'react-fontawesome';
+import classNames from 'classnames';
+import Collapse from 'react-bootstrap/lib/Collapse.js';
+
+import i18n from 'nfvo-utils/i18n/i18n.js';
+import {nodeTypes, mouseActions} from './AttachmentsConstants';
+
+const typeToIcon = Object.freeze({
+ heat: 'building-o',
+ volume: 'database',
+ network: 'cloud',
+ artifact: 'gear',
+ env: 'server',
+ other: 'cube'
+});
+
+const leftPanelWidth = 250;
+
+class SoftwareProductAttachmentsView extends React.Component {
+
+ static propTypes = {
+ attachmentsTree: React.PropTypes.object.isRequired
+ };
+ state = {
+ treeWidth: '400',
+ };
+
+ render() {
+ let {attachmentsTree, errorList = []} = this.props;
+
+ let {treeWidth} = this.state;
+ return (
+ <div className='software-product-attachments'>
+ <div className='software-product-view'>
+ <div className='software-product-landing-view-right-side'>
+ <div className='software-product-attachments-main'>
+ <div className='software-product-attachments-tree' style={{'width' : treeWidth + 'px'}}>
+ <div className='tree-wrapper'>
+ {
+ attachmentsTree && attachmentsTree.children && attachmentsTree.children.map((child, ind) => this.renderNode(child, [ind]))
+ }
+ </div>
+ </div>
+ <div className='software-product-attachments-separator' onMouseDown={e => this.onChangeTreeWidth(e)} />
+ <div className='software-product-attachments-error-list'>
+ {errorList.length ? this.renderErrorList(errorList) : <div className='no-errors'>{attachmentsTree.children ?
+ i18n('VALIDATION SUCCESS') : i18n('THERE IS NO HEAT DATA TO PRESENT') }</div>}
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+ );
+ }
+
+
+
+ renderNode(node, path) {
+ let isFolder = node.children && node.children.length > 0;
+ let {onSelectNode} = this.props;
+ return (
+ <div key={node.name} className='tree-block-inside'>
+ {
+ <div onDoubleClick={() => this.props.toggleExpanded(path)} className={this.getTreeRowClassName(node.name)}>
+ {
+ isFolder &&
+ <div onClick={() => this.props.toggleExpanded(path)} className={classNames('tree-node-expander', {'tree-node-expander-collapsed': !node.expanded})}>
+ <FontAwesome name='caret-down'/>
+ </div>
+ }
+ {
+
+ <span className='tree-node-icon'>
+ <FontAwesome name={typeToIcon[node.type]}/>
+ </span>
+ }
+ {
+
+ <span onClick={() => onSelectNode(node.name)} className={this.getTreeTextClassName(node)}>
+ {node.name}
+ </span>
+ }
+ </div>
+ }
+ {
+ isFolder &&
+ <Collapse in={node.expanded}>
+ <div className='tree-node-children'>
+ {
+ node.children.map((child, ind) => this.renderNode(child, [...path, ind]))
+ }
+ </div>
+ </Collapse>
+ }
+ </div>
+ );
+ }
+
+ createErrorList(errorList, node, parent) {
+ if (node.errors) {
+ node.errors.forEach(error => errorList.push({
+ error,
+ name: node.name,
+ parentName: parent.name,
+ type: node.type
+ }));
+ }
+ if (node.children && node.children.length) {
+ node.children.map((child) => this.createErrorList(errorList, child, node));
+ }
+ }
+
+ renderErrorList(errors) {
+ let prevError = {};
+ let {selectedNode} = this.props;
+ return errors.map(error => {
+ let isSameNodeError = error.name === prevError.name && error.parentName === prevError.parentName;
+ prevError = error;
+
+ return (
+ <div
+ key={error.name + error.errorMessage + error.parentName}
+
+ onClick={() => this.selectNode(error.name)}
+ className={classNames('error-item', {'clicked': selectedNode === error.name, 'shifted': !isSameNodeError})}>
+ <span className={classNames('error-item-file-type', {'strong': !isSameNodeError})}>
+ {
+ error.hasParent ?
+ i18n('{type} {name} in {parentName}: ', {
+ type: nodeTypes[error.type],
+ name: error.name,
+ parentName: error.parentName
+ }) :
+ i18n('{type} {name}: ', {
+ type: nodeTypes[error.type],
+ name: error.name
+ })
+ }
+ </span>
+ <span className={`error-item-file-type ${error.errorLevel}`}> {error.errorMessage} </span>
+ </div>
+ );
+ });
+ }
+
+ selectNode(currentSelectedNode) {
+ let {onUnselectNode, onSelectNode, selectedNode} = this.props;
+ if (currentSelectedNode !== selectedNode) {
+ onSelectNode(currentSelectedNode);
+ }else{
+ onUnselectNode();
+ }
+
+ }
+
+ getTreeRowClassName(name) {
+ let {hoveredNode, selectedNode} = this.props;
+ return classNames({
+ 'tree-node-row': true,
+ 'tree-node-selected': name === hoveredNode,
+ 'tree-node-clicked': name === selectedNode
+ });
+ }
+
+ getTreeTextClassName(node) {
+ let {selectedNode} = this.props;
+ return classNames({
+ 'tree-element-text': true,
+ 'error-status': node.errors,
+ 'error-status-selected': node.name === selectedNode
+ });
+ }
+
+ 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);
+ }
+ }
+}
+
+export default SoftwareProductAttachmentsView;