Merge master -> master_vca_intg 17/1017/1
authorKIRAN KASHALKAR <kiran.kashalkar@riftio.com>
Wed, 25 Jan 2017 15:50:30 +0000 (10:50 -0500)
committerKIRAN KASHALKAR <kiran.kashalkar@riftio.com>
Wed, 25 Jan 2017 15:50:30 +0000 (10:50 -0500)
Signed-off-by: KIRAN KASHALKAR <kiran.kashalkar@riftio.com>
21 files changed:
skyquake/plugins/composer/api/composer.js
skyquake/plugins/composer/src/src/components/CanvasPanel.js
skyquake/plugins/composer/src/src/components/CanvasPanelTray.js
skyquake/plugins/composer/src/src/components/ComposerApp.js
skyquake/plugins/composer/src/src/components/ConfigPrimitiveParameters/ConfigPrimitiveParameters.js [new file with mode: 0644]
skyquake/plugins/composer/src/src/components/EditDescriptorModelProperties.js
skyquake/plugins/composer/src/src/components/EditDescriptorModelPropertiesBkUp.js [new file with mode: 0644]
skyquake/plugins/composer/src/src/libraries/model/DescriptorModel.js
skyquake/plugins/composer/src/src/libraries/model/DescriptorModelFactory.js
skyquake/plugins/composer/src/src/libraries/model/DescriptorModelFields.js
skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaFactory.js
skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaProperty.js
skyquake/plugins/composer/src/src/libraries/model/DescriptorModelSerializer.js
skyquake/plugins/composer/src/src/libraries/model/descriptors/NetworkService.js
skyquake/plugins/composer/src/src/libraries/model/descriptors/VirtualNetworkFunctionAccessPoint.js [new file with mode: 0644]
skyquake/plugins/composer/src/src/libraries/model/descriptors/VirtualNetworkFunctionAccessPointMap.js [new file with mode: 0644]
skyquake/plugins/composer/src/src/libraries/utils.js
skyquake/plugins/composer/src/src/stores/ComposerAppStore.js
skyquake/plugins/composer/src/src/styles/CanvasPanel.scss
skyquake/plugins/composer/src/src/styles/CanvasPanelTray.scss
skyquake/plugins/composer/src/src/styles/EditConfigParameterMap.scss [new file with mode: 0644]

index 801873a..23345f5 100644 (file)
@@ -636,7 +636,7 @@ FileManager.job = function(req) {
             forever: constants.FOREVER_ON,
             rejectUnauthorized: false,
         }, function(error, response, body) {
-            if (utils.validateResponse('restconfAPI.streams', error, response, body, resolve, reject)) {
+            if (utils.validateResponse('FileManager.job', error, response, body, resolve, reject)) {
                 var data = JSON.parse(response.body)['rw-pkg-mgmt:download-jobs'];
                 var returnData = [];
                 data && data.job.map(function(d) {
index cb212cb..df4af5b 100644 (file)
@@ -34,9 +34,9 @@ import CanvasPanelTray from './CanvasPanelTray'
 import EditForwardingGraphPaths from './EditorForwardingGraph/EditForwardingGraphPaths'
 import SelectionManager from '../libraries/SelectionManager'
 import DescriptorModelIconFactory from '../libraries/model/IconFactory'
-
 import FileManager from './filemanager/FileManager.jsx';
 
+import ConfigPrimitiveParameters from './ConfigPrimitiveParameters/ConfigPrimitiveParameters'
 import '../styles/CanvasPanel.scss'
 
 const CanvasPanel = React.createClass({
@@ -91,6 +91,13 @@ const CanvasPanel = React.createClass({
                                        </div>
                                </div>
                        )
+        //CanvasPanelTray panel to display
+        let displayedPanel = null;
+        switch (this.props.displayedPanel) {
+            case 'forwarding' : displayedPanel = (<EditForwardingGraphPaths containers={this.props.containers} />); break;
+            case 'parameter' : displayedPanel = (<ConfigPrimitiveParameters  containers={this.props.containers} />); break;
+            default: displayedPanel = (<div><p className="welcome-message">Please select a tab</p></div>); break;
+        }
                return (
                        <div id="canvasPanelDiv" className="CanvasPanel" style={style} onDragOver={this.onDragOver} onDrop={this.onDrop}>
                                <div onDoubleClick={this.onDblClickOpenFullScreen}  className="CanvasPanelHeader panel-header" data-resizable="limit_bottom">
@@ -108,8 +115,8 @@ const CanvasPanel = React.createClass({
                                                <CanvasZoom zoom={this.props.zoom} style={{bottom: this.props.layout.bottom + 20}}/>
                                                : null
                                }
-                               <CanvasPanelTray layout={this.props.layout} show={isEditingNSD && isDescriptorView}>
-                                       <EditForwardingGraphPaths containers={this.props.containers} />
+                               <CanvasPanelTray layout={this.props.layout} displayedPanel={this.props.displayedPanel} show={isEditingNSD && isDescriptorView}>
+                                       {displayedPanel}
                                </CanvasPanelTray>
                        </div>
                );
index 57f29a0..5fde578 100644 (file)
@@ -16,13 +16,17 @@ export default function (props) {
                right: props.layout.right,
                display: props.show ? false : 'none'
        };
+    const PANEL = {
+        FORWARD: 'forwarding',
+        PARAMETER: 'parameter'
+    }
        const classNames = ClassNames('CanvasPanelTray', {'-with-transitions': !document.body.classList.contains('resizing')});
        function onClickToggleOpenClose(event) {
                if (event.defaultPrevented) return;
                event.preventDefault();
                // don't toggle if the user was resizing
                if (!uiTransient.isResizing) {
-                       CanvasPanelTrayActions.toggleOpenClose();
+                       CanvasPanelTrayActions.toggleOpenClose(event);
                }
                event.target.removeEventListener('mousemove', onMouseMove, true);
        }
@@ -37,10 +41,29 @@ export default function (props) {
        const isOpen = style.height > 25;
        return (
                <div className={classNames} data-resizable="top" data-resizable-handle-offset="4" style={style}>
-                       <h1 data-open-close-icon={isOpen ? 'open' : 'closed'} onMouseDownCapture={onMouseDown} onClick={onClickToggleOpenClose}>Forwarding Graphs</h1>
+            <div  className="CanvasPanelTray-buttons" onClick={onClickToggleOpenClose}>
+                <button
+                    style={{flex: '1 1 auto'}}
+                    className={ClassNames({'-selected': props.displayedPanel === PANEL.FORWARD})}
+                    onMouseDownCapture={onMouseDown}
+                    data-event={PANEL.FORWARD}>
+                    Forwarding Graphs
+                </button>
+
+                <button
+                    style={{flex: '1 1 auto', borderLeft: '1px solid white'}}
+                    className={ClassNames({'-selected': props.displayedPanel === PANEL.PARAMETER})}
+                    onMouseDownCapture={onMouseDown}
+                    data-event={PANEL.PARAMETER}>
+                        Config Parameter Map
+                </button>
+                <div  data-open-close-icon={isOpen ? 'open' : 'closed'}  style={{flex: '0 1', width: '40px', height: '25px', cursor: 'pointer'}} data-event='arrow'>
+
+                </div>
+            </div>
                        <div className="tray-body">
                                {props.children}
                        </div>
                </div>
        );
-}
\ No newline at end of file
+}
index b5cfa75..d626ea4 100644 (file)
@@ -62,215 +62,218 @@ const clearDragState = () => ComposerAppActions.setDragState(null);
 
 
 const ComposerApp = React.createClass({
-       mixins: [PureRenderMixin],
-       getInitialState() {
-               return ComposerAppStore.getState();
-       },
-       getDefaultProps() {
-               return {};
-       },
-       componentWillMount() {
-               if (clearLocalStorage) {
-                       window.localStorage.clear();
-               }
+    mixins: [PureRenderMixin],
+    getInitialState() {
+        return ComposerAppStore.getState();
+    },
+    getDefaultProps() {
+        return {};
+    },
+    componentWillMount() {
+        if (clearLocalStorage) {
+            window.localStorage.clear();
+        }
         if(this.item) {
             FileManagerActions.openFileManagerSockets();
         }
-               this.state.isLoading = CatalogDataStore.getState().isLoading;
-               ComposerAppStore.listen(this.onChange);
-               CatalogDataStore.listen(this.onCatalogDataChanged);
-               window.addEventListener('resize', this.resize);
-               window.onbeforeunload = this.onBeforeUnload;
-               // prevent browser from downloading any drop outside of our specific drop zones
-               window.addEventListener('dragover', preventDefault);
-               window.addEventListener('drop', preventDefault);
-               // ensure drags initiated in the app clear the state on drop
-               window.addEventListener('drop', clearDragState);
-               DeletionManager.addEventListeners();
-       },
-       componentWillUnmount() {
-               window.removeEventListener('resize', this.resize);
-               window.removeEventListener('dragover', preventDefault);
-               window.removeEventListener('drop', preventDefault);
-               window.removeEventListener('drop', clearDragState);
+        this.state.isLoading = CatalogDataStore.getState().isLoading;
+        ComposerAppStore.listen(this.onChange);
+        CatalogDataStore.listen(this.onCatalogDataChanged);
+        window.addEventListener('resize', this.resize);
+        window.onbeforeunload = this.onBeforeUnload;
+        // prevent browser from downloading any drop outside of our specific drop zones
+        window.addEventListener('dragover', preventDefault);
+        window.addEventListener('drop', preventDefault);
+        // ensure drags initiated in the app clear the state on drop
+        window.addEventListener('drop', clearDragState);
+        DeletionManager.addEventListeners();
+    },
+    componentWillUnmount() {
+        window.removeEventListener('resize', this.resize);
+        window.removeEventListener('dragover', preventDefault);
+        window.removeEventListener('drop', preventDefault);
+        window.removeEventListener('drop', clearDragState);
         FileManagerActions.closeFileManagerSockets();
-               // resizeManager automatically registered its event handlers
-               resizeManager.removeAllEventListeners();
-               ComposerAppStore.unlisten(this.onChange);
-               CatalogDataStore.unlisten(this.onCatalogDataChanged);
-               DeletionManager.removeEventListeners();
-               TooltipManager.removeEventListeners();
-       },
-       componentDidMount() {
-               resizeManager.addAllEventListeners();
-               const snapshot = window.localStorage.getItem('composer');
-               if (snapshot) {
-                       alt.bootstrap(snapshot);
-               }
-               document.body.addEventListener('keydown', (event) => {
-                       // prevent details editor form from blowing up the app
-                       const ENTER_KEY = 13;
-                       if (event.which === ENTER_KEY) {
-                               event.preventDefault();
-                               return false;
-                       }
-               });
-               const appRootElement = ReactDOM.findDOMNode(this.refs.appRoot);
-               TooltipManager.addEventListeners(appRootElement);
-               SelectionManager.onClearSelection = () => {
-                       if (this.state.item) {
-                               CatalogItemsActions.catalogItemMetaDataChanged.defer(this.state.item);
-                       }
-               };
-       },
-       componentDidUpdate() {
-               if (this.state.fullScreenMode) {
-                       document.body.classList.add('-is-full-screen');
-               } else {
-                       document.body.classList.remove('-is-full-screen');
-               }
-               SelectionManager.refreshOutline();
-       },
-       resize(e) {
-               PanelResizeAction.resize(e);
-       },
-       getModel() {
-               let html;
-               let self = this;
-               DescriptorModelMetaFactory.init().then(function(){
+        // resizeManager automatically registered its event handlers
+        resizeManager.removeAllEventListeners();
+        ComposerAppStore.unlisten(this.onChange);
+        CatalogDataStore.unlisten(this.onCatalogDataChanged);
+        DeletionManager.removeEventListeners();
+        TooltipManager.removeEventListeners();
+    },
+    componentDidMount() {
+        resizeManager.addAllEventListeners();
+        const snapshot = window.localStorage.getItem('composer');
+        if (snapshot) {
+            alt.bootstrap(snapshot);
+        }
+        document.body.addEventListener('keydown', (event) => {
+            // prevent details editor form from blowing up the app
+            const ENTER_KEY = 13;
+            if (event.which === ENTER_KEY) {
+                event.preventDefault();
+                return false;
+            }
+        });
+        const appRootElement = ReactDOM.findDOMNode(this.refs.appRoot);
+        TooltipManager.addEventListeners(appRootElement);
+        SelectionManager.onClearSelection = () => {
+            if (this.state.item) {
+                CatalogItemsActions.catalogItemMetaDataChanged.defer(this.state.item);
+            }
+        };
+    },
+    componentDidUpdate() {
+        if (this.state.fullScreenMode) {
+            document.body.classList.add('-is-full-screen');
+        } else {
+            document.body.classList.remove('-is-full-screen');
+        }
+        SelectionManager.refreshOutline();
+    },
+    resize(e) {
+        PanelResizeAction.resize(e);
+    },
+    getModel() {
+        let html;
+        let self = this;
+        DescriptorModelMetaFactory.init().then(function(){
 
-                       self.setState({
-                               hasModel: true
-                       })
-               });
-       },
-       render() {
-               let html = null;
-               let self = this;
-               if(this.state.hasModel) {
+            self.setState({
+                hasModel: true
+            })
+        });
+    },
+    render() {
+        let html = null;
+        let self = this;
+        if(this.state.hasModel) {
 
-                       function onClickUpdateSelection(event) {
-                               if (event.defaultPrevented) {
-                                       return
-                               }
-                               const element = SelectionManager.getClosestElementWithUID(event.target);
-                               if (element) {
-                                       SelectionManager.select(element);
-                                       SelectionManager.refreshOutline();
-                                       event.preventDefault();
-                               } else {
-                                       SelectionManager.clearSelectionAndRemoveOutline();
-                               }
-                       }
+            function onClickUpdateSelection(event) {
+                if (event.defaultPrevented) {
+                    return
+                }
+                const element = SelectionManager.getClosestElementWithUID(event.target);
+                if (element) {
+                    SelectionManager.select(element);
+                    SelectionManager.refreshOutline();
+                    event.preventDefault();
+                } else {
+                    if(event.target.offsetParent && !event.target.offsetParent.classList.contains("tray-body")) {
+                        SelectionManager.clearSelectionAndRemoveOutline();
+                    }
+                }
+            }
 
-                       let cpNumber = 0;
-                       let AppHeader = (<div className="AppHeader">
-                                                               <RiftHeader />
-                                                       </div>);
-                       // AppHeader = null;
-                       const classNames = ClassNames('ComposerApp');
-                       const isNew = self.state.item && self.state.item.uiState.isNew;
-                       const hasItem = self.state.item && self.state.item.uiState;
-                       const isModified = self.state.item && self.state.item.uiState.modified;
-                       const isEditingNSD = self.state.item && self.state.item.uiState && /nsd/.test(self.state.item.uiState.type);
-                       const isEditingVNFD = self.state.item && self.state.item.uiState && /vnfd/.test(self.state.item.uiState.type);
-                       const containers = [self.state.item].reduce(DescriptorModelFactory.buildCatalogItemFactory(CatalogDataStore.getState().catalogs), []);
+            let cpNumber = 0;
+            let AppHeader = (<div className="AppHeader">
+                                <RiftHeader />
+                            </div>);
+            // AppHeader = null;
+            const classNames = ClassNames('ComposerApp');
+            const isNew = self.state.item && self.state.item.uiState.isNew;
+            const hasItem = self.state.item && self.state.item.uiState;
+            const isModified = self.state.item && self.state.item.uiState.modified;
+            const isEditingNSD = self.state.item && self.state.item.uiState && /nsd/.test(self.state.item.uiState.type);
+            const isEditingVNFD = self.state.item && self.state.item.uiState && /vnfd/.test(self.state.item.uiState.type);
+            const containers = [self.state.item].reduce(DescriptorModelFactory.buildCatalogItemFactory(CatalogDataStore.getState().catalogs), []);
 
-                       containers.filter(d => DescriptorModelFactory.isConnectionPoint(d)).forEach(d => {
-                               d.cpNumber = ++cpNumber;
-                               containers.filter(d => DescriptorModelFactory.isVnfdConnectionPointRef(d)).filter(ref => ref.key === d.key).forEach(ref => ref.cpNumber = d.cpNumber);
-                       });
-                       const canvasTitle = containers.length ? containers[0].model.name : '';
-                       const hasNoCatalogs = CatalogDataStore.getState().catalogs.length === 0;
-                       const isLoading = self.state.isLoading;
+            containers.filter(d => DescriptorModelFactory.isConnectionPoint(d)).forEach(d => {
+                d.cpNumber = ++cpNumber;
+                containers.filter(d => DescriptorModelFactory.isVnfdConnectionPointRef(d)).filter(ref => ref.key === d.key).forEach(ref => ref.cpNumber = d.cpNumber);
+            });
+            const canvasTitle = containers.length ? containers[0].model.name : '';
+            const hasNoCatalogs = CatalogDataStore.getState().catalogs.length === 0;
+            const isLoading = self.state.isLoading;
 
             //Bridge element for Crouton fix. Should eventually put Composer on same flux context
             const Bridge = this.state.ComponentBridgeElement;
 
-                       html = (
-                               <div ref="appRoot" id="RIFT_wareLaunchpadComposerAppRoot" className="AppRoot" onClick={onClickUpdateSelection}>
+            html = (
+                <div ref="appRoot" id="RIFT_wareLaunchpadComposerAppRoot" className="AppRoot" onClick={onClickUpdateSelection}>
                     <Bridge />
-                                       <i className="corner-accent top left" />
-                                       <i className="corner-accent top right" />
-                                       <i className="corner-accent bottom left" />
-                                       <i className="corner-accent bottom right" />
-                                       {AppHeader}
-                                       <div className="AppBody">
-                                               <div className={classNames}>
-                                                       <CatalogPanel layout={self.state.layout}
-                                                                                 isLoading={isLoading}
-                                                                                 hasNoCatalogs={hasNoCatalogs}
-                                                                                 filterByType={self.state.filterCatalogByTypeValue} />
-                                                       <CanvasPanel layout={self.state.layout}
-                                                                                hasNoCatalogs={hasNoCatalogs}
-                                                                                showMore={self.state.showMore}
-                                                                                containers={containers}
-                                                                                title={canvasTitle}
-                                                                                zoom={self.state.zoom}
-                                                                                panelTabShown={self.state.panelTabShown}
-                                                                                files={self.state.files}
-                                                                                filesState={self.state.filesState}
-                                                                                item={self.state.item}
-                                                                                type={self.state.filterCatalogByTypeValue}
-                                                                                 />
-                                                       {
-                                                               (self.state.panelTabShown == 'descriptor') ?
-                                                               <DetailsPanel layout={self.state.layout}
-                                                                                 hasNoCatalogs={hasNoCatalogs}
-                                                                                 showMore={self.state.showMore}
-                                                                                 containers={containers}
-                                                                                 showJSONViewer={self.state.showJSONViewer} />
-                                                         : null
-                                                       }
+                    <i className="corner-accent top left" />
+                    <i className="corner-accent top right" />
+                    <i className="corner-accent bottom left" />
+                    <i className="corner-accent bottom right" />
+                    {AppHeader}
+                    <div className="AppBody">
+                        <div className={classNames}>
+                            <CatalogPanel layout={self.state.layout}
+                                          isLoading={isLoading}
+                                          hasNoCatalogs={hasNoCatalogs}
+                                          filterByType={self.state.filterCatalogByTypeValue} />
+                            <CanvasPanel layout={self.state.layout}
+                                         hasNoCatalogs={hasNoCatalogs}
+                                         showMore={self.state.showMore}
+                                         containers={containers}
+                                         title={canvasTitle}
+                                         zoom={self.state.zoom}
+                                         panelTabShown={self.state.panelTabShown}
+                                         files={self.state.files}
+                                         filesState={self.state.filesState}
+                                         item={self.state.item}
+                                         type={self.state.filterCatalogByTypeValue}
+                                         displayedPanel={self.state.displayedPanel}
+                                          />
+                            {
+                                (self.state.panelTabShown == 'descriptor') ?
+                                <DetailsPanel layout={self.state.layout}
+                                          hasNoCatalogs={hasNoCatalogs}
+                                          showMore={self.state.showMore}
+                                          containers={containers}
+                                          showJSONViewer={self.state.showJSONViewer} />
+                              : null
+                            }
 
-                                                       <ComposerAppToolbar layout={self.state.layout}
-                                                                                               showMore={self.state.showMore}
-                                                                                               isEditingNSD={isEditingNSD}
-                                                                                               isEditingVNFD={isEditingVNFD}
-                                                                                               isModified={isModified}
-                                                                                               isNew={isNew}
-                                                                                               disabled={!hasItem}
-                                                                                               onClick={event => event.stopPropagation()}
-                                                                                               panelTabShown={self.state.panelTabShown}/>
-                                               </div>
-                                       </div>
-                                       <ModalOverlay />
-                               </div>
-                       );
-               } else {
-                       this.getModel();
-               }
-               return html;
-       },
-       onChange(state) {
-               this.setState(state);
-       },
-       onCatalogDataChanged(catalogDataState) {
-               const catalogs = catalogDataState.catalogs;
-               const unsavedChanges = catalogs.reduce((result, catalog) => {
-                       if (result) {
-                               return result;
-                       }
-                       return catalog.descriptors.reduce((result, descriptor) => {
-                               if (result) {
-                                       return result;
-                               }
-                               return descriptor.uiState.modified;
-                       }, false);
-               }, false);
-               this.setState({
-                       unsavedChanges: unsavedChanges,
-                       isLoading: catalogDataState.isLoading
-               });
-       },
-       onBeforeUnload() {
-               // https://trello.com/c/c8v321Xx/160-prompt-user-to-save-changes
-               //const snapshot = alt.takeSnapshot();
-               //window.localStorage.setItem('composer', snapshot);
-               if (this.state.unsavedChanges) {
-                       return 'You have unsaved changes. If you do not onboard (or update) your changes they will be lost.';
-               }
-       }
+                            <ComposerAppToolbar layout={self.state.layout}
+                                                showMore={self.state.showMore}
+                                                isEditingNSD={isEditingNSD}
+                                                isEditingVNFD={isEditingVNFD}
+                                                isModified={isModified}
+                                                isNew={isNew}
+                                                disabled={!hasItem}
+                                                onClick={event => event.stopPropagation()}
+                                                panelTabShown={self.state.panelTabShown}/>
+                        </div>
+                    </div>
+                    <ModalOverlay />
+                </div>
+            );
+        } else {
+            this.getModel();
+        }
+        return html;
+    },
+    onChange(state) {
+        this.setState(state);
+    },
+    onCatalogDataChanged(catalogDataState) {
+        const catalogs = catalogDataState.catalogs;
+        const unsavedChanges = catalogs.reduce((result, catalog) => {
+            if (result) {
+                return result;
+            }
+            return catalog.descriptors.reduce((result, descriptor) => {
+                if (result) {
+                    return result;
+                }
+                return descriptor.uiState.modified;
+            }, false);
+        }, false);
+        this.setState({
+            unsavedChanges: unsavedChanges,
+            isLoading: catalogDataState.isLoading
+        });
+    },
+    onBeforeUnload() {
+        // https://trello.com/c/c8v321Xx/160-prompt-user-to-save-changes
+        //const snapshot = alt.takeSnapshot();
+        //window.localStorage.setItem('composer', snapshot);
+        if (this.state.unsavedChanges) {
+            return 'You have unsaved changes. If you do not onboard (or update) your changes they will be lost.';
+        }
+    }
 
 });
 
diff --git a/skyquake/plugins/composer/src/src/components/ConfigPrimitiveParameters/ConfigPrimitiveParameters.js b/skyquake/plugins/composer/src/src/components/ConfigPrimitiveParameters/ConfigPrimitiveParameters.js
new file mode 100644 (file)
index 0000000..912895c
--- /dev/null
@@ -0,0 +1,353 @@
+
+/*
+ *
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   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.
+ *
+ */
+
+
+'use strict';
+
+import _ from 'lodash'
+import d3 from 'd3'
+import React from 'react'
+import Range from '../Range'
+import Button from '../Button'
+import ClassNames from 'classnames'
+import changeCase from 'change-case'
+import LayoutRow from '../LayoutRow'
+import SelectionManager from '../../libraries/SelectionManager'
+import PureRenderMixin from 'react-addons-pure-render-mixin'
+import CatalogItemsActions from '../../actions/CatalogItemsActions'
+import CanvasEditorActions from '../../actions/CanvasEditorActions'
+import DescriptorModelFactory from '../../libraries/model/DescriptorModelFactory'
+import ComposerAppActions from '../../actions/ComposerAppActions'
+import DescriptorModelMetaFactory from '../../libraries/model/DescriptorModelMetaFactory'
+import ComposerAppStore from '../../stores/ComposerAppStore'
+import DeletionManager from '../../libraries/DeletionManager'
+import ContentEditableDiv from '../ContentEditableDiv'
+import TooltipManager from '../../libraries/TooltipManager'
+import HighlightRecordServicePaths from '../../libraries/graph/HighlightRecordServicePaths'
+
+import '../../styles/EditForwardingGraphPaths.scss'
+
+import imgNSD from '../../images/default-catalog-icon.svg'
+import imgFG from '../../../../node_modules/open-iconic/svg/infinity.svg'
+import imgRemove from '../../../../node_modules/open-iconic/svg/trash.svg'
+import imgAdd from '../../../../node_modules/open-iconic/svg/plus.svg'
+import imgConnection from '../../../../node_modules/open-iconic/svg/random.svg'
+import imgClassifier from '../../../../node_modules/open-iconic/svg/spreadsheet.svg'
+import imgReorder from '../../../../node_modules/open-iconic/svg/menu.svg'
+import CatalogDataStore from '../../stores/CatalogDataStore'
+import utils from '../../libraries/utils'
+import getEventPath from '../../libraries/getEventPath'
+import guid from '../../libraries/guid'
+
+import '../../styles/EditDescriptorModelProperties.scss'
+import '../../styles/EditConfigParameterMap.scss';
+
+function configParameterMapMap(ap, i) {
+
+    const context = this;
+    context.vnfapMap = ap;
+    return (
+        <div key={i}>
+        <div>{ap.id}</div>
+        <div>{ap.capability['member-vnf-index']}</div>
+        <div>{ap.capability['capability-ref']}</div>
+
+        </div>
+    )
+
+}
+
+function mapNSD(nsd, i) {
+
+    const context = this;
+    context.nsd = nsd;
+
+    function onClickAddConfigParameterMap(nsd, event) {
+        event.preventDefault();
+        nsd.createConfigParameterMap();
+        CatalogItemsActions.catalogItemDescriptorChanged(nsd.getRoot());
+    }
+
+    const forwardingGraphs = nsd.configParameterMap.map(configParameterMap.bind(context));
+    if (forwardingGraphs.length === 0) {
+        forwardingGraphs.push(
+            <div key="1" className="welcome-message">
+                No Forwarding Graphs to model.
+            </div>
+        );
+    }
+
+    return (
+        <div key={i} className={nsd.className}>
+            {forwardingGraphs}
+            <div className="footer-actions">
+                <div className="row-action-column">
+                    <Button className="create-new-forwarding-graph" src={imgAdd} width="20px" onClick={onClickAddConfigParameterMap.bind(null, nsd)} label="Add new Access Point" />
+                </div>
+            </div>
+        </div>
+    );
+
+}
+
+
+function startEditing() {
+        event.stopPropagation();
+        DeletionManager.removeEventListeners();
+    }
+
+function endEditing() {
+    DeletionManager.addEventListeners();
+}
+
+
+const ConfigPrimitiveParameters = React.createClass({
+    mixins: [PureRenderMixin],
+    getInitialState: function () {
+        return ComposerAppStore.getState();
+    },
+    getDefaultProps: function () {
+        return {
+            containers: []
+        };
+    },
+    componentWillMount: function () {
+    },
+    componentDidMount: function () {
+    },
+    componentDidUpdate: function () {
+    },
+    componentWillUnmount: function () {
+    },
+    render() {
+        const self = this;
+        const containers = this.props.containers;
+        let NSContainer = containers.filter(function(c) {
+           return c.className == "NetworkService"
+        })[0]
+        const context = {
+            component: this,
+            containers: containers
+        };
+
+        const networkService = containers.filter(d => d.type === 'nsd');
+        if (networkService.length === 0) {
+            return <p className="welcome-message">No <img src={imgNSD} width="20px" /> NSD open in the canvas. Try opening an NSD.</p>;
+        }
+        let MapData = constructRequestSourceData(containers);
+        let mapCounter = 1;
+
+
+
+        return (
+                <div className="ConfigParameterMap">
+                    <div className="config-parameter-map">
+                        <div className="config-parameter-titles">
+                            <div className="config-parameter">
+                                Request
+                            </div>
+                            <div className="config-parameter">
+                                Source
+                            </div>
+                        </div>
+                        <div className="config-parameter-map">
+                            {
+                                MapData.Requests.map(function(r, i) {
+                                    let currentValue = {};
+                                    let SourceOptions = [<option value={JSON.stringify({
+                                            requestValue: r.name,
+                                            requestIndex: r.vnfdIndex
+                                        })} key="reset">No Source Selected</option>]
+                                    MapData.Sources.map(function(s, j) {
+                                        let value = {
+                                            value: s.name,
+                                            index: s.vnfdIndex,
+                                            requestValue: r.name,
+                                            requestIndex: r.vnfdIndex
+                                        }
+                                        SourceOptions.push(<option value={JSON.stringify(value)} key={`${j}-${i}`} >{`${s.vnfdName} (${s.vnfdIndex}) / ${s.name}`}</option>)
+                                    })
+                                    //Finds current value
+                                    NSContainer.model['config-parameter-map'] && NSContainer.model['config-parameter-map'].map((c)=>{
+                                        if(
+                                            c['config-parameter-request'] &&
+                                            (c['config-parameter-request']['config-parameter-request-ref'] == r.name)
+                                            && (c['config-parameter-request']['member-vnf-index-ref'] == r.vnfdIndex)
+                                           ) {
+                                            currentValue = {
+                                                value: c['config-parameter-source']['config-parameter-source-ref'],
+                                                index: c['config-parameter-source']['member-vnf-index-ref'],
+                                                requestValue: r.name,
+                                                requestIndex: r.vnfdIndex
+                                            };
+                                        }
+                                    })
+                                    currentValue.hasOwnProperty('value') ? mapCounter++ : mapCounter--;
+                                    let currentMapIndex = (mapCounter > 0) ? (mapCounter) - 1: 0;
+                                    return (
+                                            <div key={i} className="EditDescriptorModelProperties -is-tree-view config-parameter config-parameter-group">
+                                                <div  className="config-parameter-request" >{`${r.vnfdName} (${r.vnfdIndex}) / ${r.parameter && r.parameter[0]['config-primitive-name-ref']} / ${r.parameter && r.parameter[0]['config-primitive-parameter-ref']}`}</div>
+                                                <div className="config-parameter-source">
+                                                    <select
+                                                        onChange={onFormFieldValueChanged.bind(NSContainer, i)}
+                                                        onBlur={endEditing}
+                                                        onMouseDown={startEditing}
+                                                        onMouseOver={startEditing}
+                                                        value={JSON.stringify(currentValue)}
+                                                         >
+                                                        }
+                                                        {SourceOptions}
+                                                    </select>
+                                                </div>
+                                            </div>
+                                    )
+                                })
+                            }
+                        </div>
+                    </div>
+                </div>
+        )
+    }
+});
+
+    function onFormFieldValueChanged(index, event) {
+        if (DescriptorModelFactory.isContainer(this)) {
+            event.preventDefault();
+            const name = event.target.name;
+            const value = JSON.parse(event.target.value);
+
+            let ConfigMap = utils.resolvePath(this.model, 'config-parameter-map');
+            let ConfigMapIndex = false;
+            let id = guid().substring(0, 8);
+            //Check current map, if request is present, assign map index.
+            ConfigMap.map(function(c, i) {
+                let req = c['config-parameter-request'];
+                if((req['config-parameter-request-ref'] == value.requestValue) &&
+                   (req['member-vnf-index-ref'] == value.requestIndex)) {
+                    ConfigMapIndex = i;
+                    id = c.id;
+                }
+            });
+            if(!ConfigMapIndex && _.isBoolean(ConfigMapIndex)) {
+                ConfigMapIndex = ConfigMap.length;
+            }
+            if(value.value) {
+                utils.assignPathValue(this.model, 'config-parameter-map.' + ConfigMapIndex + '.config-parameter-source.config-parameter-source-ref', value.value);
+                utils.assignPathValue(this.model, 'config-parameter-map.' + ConfigMapIndex + '.config-parameter-source.member-vnf-index-ref', value.index);
+                utils.assignPathValue(this.model, 'config-parameter-map.' + ConfigMapIndex + '.config-parameter-request.config-parameter-request-ref', value.requestValue);
+                utils.assignPathValue(this.model, 'config-parameter-map.' + ConfigMapIndex + '.config-parameter-request.member-vnf-index-ref', value.requestIndex);
+                utils.assignPathValue(this.model, 'config-parameter-map.' + ConfigMapIndex + '.id', id);
+                CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+            } else {
+                utils.removePathValue(this.model, 'config-parameter-map.' + ConfigMapIndex)
+                CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+
+            }
+        }
+    }
+
+
+//Values from
+//
+
+//To update
+//Container:NSD
+//path
+//["config-parameter", "config-parameter-source"]
+//{config-parameter-source-ref: "service_port", member-vnf-index-ref: 2}
+
+function constructRequestSourceData(containers) {
+    let cds = CatalogDataStore;
+    let catalogs = cds.getTransientCatalogs();
+    let Requests = [];
+    let Sources = [];
+    let vnfdData = {
+        index:[],
+        vnfdIDs:[],
+        indexRefs: {},
+        vnfdRefs:{}
+    };
+
+    //Init VNFD map
+    //{
+    //
+    //  index:[1], //member-vnfd-index-ref
+    //  vnfdIDs:[],
+    //  indexRefs: {
+    //      1: vnfdID
+    //  },
+    //  vnfdRefs: {
+    //      {1.id} : {...}
+    //  }
+    //}
+
+    containers.map(function(c, i) {
+        if(c.className == 'ConstituentVnfd') {
+            vnfdData.index.push(c.vnfdIndex);
+            vnfdData.vnfdIDs.push(c.vnfdId);
+            vnfdData.indexRefs[c.vnfdIndex] = c.vnfdId;
+            vnfdData.vnfdRefs[c.vnfdId] = {
+                id: c.vnfdId,
+                name: c.name,
+                'short-name': c['short-name']
+            };
+        }
+    });
+
+    //Decorate VNFDMap with descriptor data;
+    catalogs[1].descriptors
+        .filter((v) => vnfdData.vnfdIDs.indexOf(v.id) > -1)
+        .map(constructVnfdMap.bind(this, vnfdData));
+
+
+    vnfdData.index.map(function(vnfdIndex) {
+        let vnfdId = vnfdData.indexRefs[vnfdIndex];
+        let vnfd = vnfdData.vnfdRefs[vnfdId];
+        let vnfdShortName = vnfd['short-name'];
+        vnfd.requests && vnfd.requests.map(function(request) {
+            Requests.push(_.merge({
+                            id: vnfdId,
+                            vnfdIndex: vnfdIndex,
+                            vnfdName: vnfdShortName,
+                        }, request))
+        });
+        vnfd.sources && vnfd.sources.map(function(source) {
+            Sources.push(_.merge({
+                            id: vnfdId,
+                            vnfdIndex: vnfdIndex,
+                            vnfdName: vnfdShortName,
+                        }, source));
+        });
+    })
+
+    return {Requests, Sources};
+
+    function constructVnfdMap(vnfdData, vnfd) {
+        let data = {
+            requests: vnfd['config-parameter']['config-parameter-request'],
+            sources: vnfd['config-parameter']['config-parameter-source']
+        };
+        vnfdData.vnfdRefs[vnfd.id] =  _.merge(vnfdData.vnfdRefs[vnfd.id], data);
+    }
+
+}
+
+
+
+export default ConfigPrimitiveParameters;
index 4ee4345..2a5da21 100644 (file)
@@ -46,6 +46,8 @@ import imgRemove from '../../../node_modules/open-iconic/svg/trash.svg'
 
 import '../styles/EditDescriptorModelProperties.scss'
 
+
+
 function getDescriptorMetaBasicForType(type) {
        const basicPropertiesFilter = d => _.includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
        return DescriptorModelMetaFactory.getModelMetaForType(type, basicPropertiesFilter) || {properties: []};
@@ -70,16 +72,7 @@ function getTitle(model = {}) {
                return model.id;
        }
 }
-
-export default function EditDescriptorModelProperties(props) {
-
-       const container = props.container;
-
-       if (!(DescriptorModelFactory.isContainer(container))) {
-               return
-       }
-
-       function startEditing() {
+function startEditing() {
                DeletionManager.removeEventListeners();
        }
 
@@ -262,7 +255,7 @@ export default function EditDescriptorModelProperties(props) {
                });
        }
 
-       function buildChoice(container, property, path, value, key) {
+    function buildChoice(container, property, path, value, key, props={}) {
 
                function onFormFieldValueChanged(event) {
                        if (DescriptorModelFactory.isContainer(this)) {
@@ -516,15 +509,15 @@ export default function EditDescriptorModelProperties(props) {
                        if (isMissingDescriptorMeta) {
                                field = <span key={key.concat('warning').join(':')} className="warning">No Descriptor Meta for {property.name}</span>;
                        } else if (property.type === 'choice') {
-                               field = buildChoice(container, property, valuePath, value, key.join(':'));
+                field = buildChoice(container, property, valuePath, value, key.join(':'), props);
                        } else if (isSimpleListView) {
-                               field = buildSimpleListItem(container, property, valuePath, value, key, index);
+                field = buildSimpleListItem(container, property, valuePath, value, key, index, props);
                        } else if (isLeafList) {
-                               field = buildLeafListItem(container, property, valuePath, value, key, index);
+                field = buildLeafListItem(container, property, valuePath, value, key, index, props);
                        } else if (hasProperties) {
-                               field = buildElement(container, property, valuePath, value, key.join(':'))
+                field = buildElement(container, property, valuePath, value, key.join(':'), props)
                        } else {
-                               field = buildField(container, property, valuePath, value, key.join(':'));
+                field = buildField(container, property, valuePath, value, key.join(':'), props);
                        }
 
                        function onClickLeaf(property, path, value, event) {
@@ -594,8 +587,17 @@ export default function EditDescriptorModelProperties(props) {
                );
 
        }
+export default function EditDescriptorModelProperties(props, type) {
+
+    const container = props.container;
+
+    if (!(DescriptorModelFactory.isContainer(container))) {
+        return
+    }
+
+
 
-       const containerType = container.uiState['qualified-type'] || container.uiState.type;
+    const containerType = (_.isEmpty(type) ? false : type)|| container.uiState['qualified-type'] || container.uiState.type;
        const basicProperties = getDescriptorMetaBasicForType(containerType).properties;
 
        function buildBasicGroup() {
@@ -633,7 +635,7 @@ export default function EditDescriptorModelProperties(props) {
                                        {properties.map(property => {
                                                const path = [property.name];
                                                const value = container.model[property.name];
-                                               return build(container, property, path, value, {toggle: true, width: props.width});
+                        return build(container, property, path, value, _.assign({toggle: true, width: props.width}, props));
                                        })}
                                </div>
                                <div className="toggle-bottom-spacer" style={{visibility: 'hidden', 'height': '50%', position: 'absolute'}}>We need this so when the user closes the panel it won't shift away and scare the bj out of them!</div>
@@ -665,3 +667,6 @@ export default function EditDescriptorModelProperties(props) {
        );
 
 }
+export {build}
+// export buildElement;
+// export buildChoice;
diff --git a/skyquake/plugins/composer/src/src/components/EditDescriptorModelPropertiesBkUp.js b/skyquake/plugins/composer/src/src/components/EditDescriptorModelPropertiesBkUp.js
new file mode 100644 (file)
index 0000000..c8e664a
--- /dev/null
@@ -0,0 +1,671 @@
+/*
+ *
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   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.
+ *
+ */
+/**
+ * Created by onvelocity on 1/18/16.
+ *
+ * This class generates the form fields used to edit the CONFD JSON model.
+ */
+'use strict';
+
+import _ from 'lodash'
+import utils from '../libraries/utils'
+import React from 'react'
+import ClassNames from 'classnames'
+import changeCase from 'change-case'
+import toggle from '../libraries/ToggleElementHandler'
+import Button from './Button'
+import Property from '../libraries/model/DescriptorModelMetaProperty'
+import ComposerAppActions from '../actions/ComposerAppActions'
+import CatalogItemsActions from '../actions/CatalogItemsActions'
+import DESCRIPTOR_MODEL_FIELDS from '../libraries/model/DescriptorModelFields'
+import DescriptorModelFactory from '../libraries/model/DescriptorModelFactory'
+import DescriptorModelMetaFactory from '../libraries/model/DescriptorModelMetaFactory'
+import SelectionManager from '../libraries/SelectionManager'
+import DeletionManager from '../libraries/DeletionManager'
+import DescriptorModelIconFactory from '../libraries/model/IconFactory'
+import getEventPath from '../libraries/getEventPath'
+import CatalogDataStore from '../stores/CatalogDataStore'
+
+import imgAdd from '../../../node_modules/open-iconic/svg/plus.svg'
+import imgRemove from '../../../node_modules/open-iconic/svg/trash.svg'
+
+import '../styles/EditDescriptorModelProperties.scss'
+
+
+
+function getDescriptorMetaBasicForType(type) {
+    const basicPropertiesFilter = d => _.contains(DESCRIPTOR_MODEL_FIELDS[type], d.name);
+    return DescriptorModelMetaFactory.getModelMetaForType(type, basicPropertiesFilter) || {properties: []};
+}
+
+function getDescriptorMetaAdvancedForType(type) {
+    const advPropertiesFilter = d => !_.contains(DESCRIPTOR_MODEL_FIELDS[type], d.name);
+    return DescriptorModelMetaFactory.getModelMetaForType(type, advPropertiesFilter) || {properties: []};
+}
+
+function getTitle(model = {}) {
+    if (typeof model['short-name'] === 'string' && model['short-name']) {
+        return model['short-name'];
+    }
+    if (typeof model.name === 'string' && model.name) {
+        return model.name;
+    }
+    if (model.uiState && typeof model.uiState.displayName === 'string' && model.uiState.displayName) {
+        return model.uiState.displayName
+    }
+    if (typeof model.id === 'string') {
+        return model.id;
+    }
+}
+
+export default function EditDescriptorModelProperties(props) {
+
+    const container = props.container;
+
+    if (!(DescriptorModelFactory.isContainer(container))) {
+        return
+    }
+
+    function startEditing() {
+        DeletionManager.removeEventListeners();
+    }
+
+    function endEditing() {
+        DeletionManager.addEventListeners();
+    }
+
+    function onClickSelectItem(property, path, value, event) {
+        event.preventDefault();
+        const root = this.getRoot();
+        if (SelectionManager.select(value)) {
+            CatalogItemsActions.catalogItemMetaDataChanged(root.model);
+        }
+    }
+
+    function onFocusPropertyFormInputElement(property, path, value, event) {
+
+        event.preventDefault();
+        startEditing();
+
+        function removeIsFocusedClass(event) {
+            event.target.removeEventListener('blur', removeIsFocusedClass);
+            Array.from(document.querySelectorAll('.-is-focused')).forEach(d => d.classList.remove('-is-focused'));
+        }
+
+        removeIsFocusedClass(event);
+
+        const propertyWrapper = getEventPath(event).reduce((parent, element) => {
+            if (parent) {
+                return parent;
+            }
+            if (!element.classList) {
+                return false;
+            }
+            if (element.classList.contains('property')) {
+                return element;
+            }
+        }, false);
+
+        if (propertyWrapper) {
+            propertyWrapper.classList.add('-is-focused');
+            event.target.addEventListener('blur', removeIsFocusedClass);
+        }
+
+    }
+
+    function buildAddPropertyAction(container, property, path) {
+        function onClickAddProperty(property, path, event) {
+            event.preventDefault();
+            //SelectionManager.resume();
+            const create = Property.getContainerCreateMethod(property, this);
+            if (create) {
+                const model = null;
+                create(model, path, property);
+            } else {
+                const name = path.join('.');
+                const value = Property.createModelInstance(property);
+                utils.assignPathValue(this.model, name, value);
+            }
+            CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+        }
+        return (
+                <Button className="inline-hint" onClick={onClickAddProperty.bind(container, property, path)} label="Add" src={imgAdd} />
+        );
+    }
+
+    function buildRemovePropertyAction(container, property, path) {
+        function onClickRemoveProperty(property, path, event) {
+            event.preventDefault();
+            const name = path.join('.');
+            const removeMethod = Property.getContainerMethod(property, this, 'remove');
+            if (removeMethod) {
+                removeMethod(utils.resolvePath(this.model, name));
+            } else {
+                utils.removePathValue(this.model, name);
+            }
+            CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+        }
+        return (
+            <Button className="remove-property-action inline-hint" title="Remove" onClick={onClickRemoveProperty.bind(container, property, path)} label="Remove" src={imgRemove}/>
+        );
+    }
+
+    function onFormFieldValueChanged(event) {
+        if (DescriptorModelFactory.isContainer(this)) {
+            event.preventDefault();
+            const name = event.target.name;
+            const value = event.target.value;
+            utils.assignPathValue(this.model, name, value);
+            CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+        }
+    }
+
+    function buildField(container, property, path, value, fieldKey) {
+        let cds = CatalogDataStore;
+        let catalogs = cds.getTransientCatalogs();
+
+        const name = path.join('.');
+        const isEditable = true;
+        const isGuid = Property.isGuid(property);
+        const onChange = onFormFieldValueChanged.bind(container);
+        const isEnumeration = Property.isEnumeration(property);
+        const isLeafRef = Property.isLeafRef(property);
+        const onFocus = onFocusPropertyFormInputElement.bind(container, property, path, value);
+        const placeholder = changeCase.title(property.name);
+        const className = ClassNames(property.name + '-input', {'-is-guid': isGuid});
+        const fieldValue = value ? (value.constructor.name != "Object") ? value : '' : undefined;
+        if (isEnumeration) {
+            const enumeration = Property.getEnumeration(property, value);
+            const options = enumeration.map((d, i) => {
+                // note yangforge generates values for enums but the system does not use them
+                // so we categorically ignore them
+                // https://trello.com/c/uzEwVx6W/230-bug-enum-should-not-use-index-only-name
+                //return <option key={fieldKey + ':' + i} value={d.value}>{d.name}</option>;
+                return <option key={fieldKey.toString() + ':' + i} value={d.name}>{d.name}</option>;
+            });
+            const isValueSet = enumeration.filter(d => d.isSelected).length > 0;
+            if (!isValueSet || property.cardinality === '0..1') {
+                const noValueDisplayText = changeCase.title(property.name);
+                options.unshift(<option key={'(value-not-in-enum)' + fieldKey.toString()} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
+            }
+            return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={value} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} readOnly={!isEditable}>{options}</select>;
+        }
+
+        if (isLeafRef) {
+            let fullFieldKey = fieldKey;
+            let containerRef = container;
+            while (containerRef.parent) {
+                fullFieldKey = containerRef.parent.key + ':' + fullFieldKey;
+                containerRef = containerRef.parent;
+            }
+            const leafRefPathValues = Property.getLeafRef(property, path, value, fullFieldKey, catalogs, container);
+
+            const options = leafRefPathValues && leafRefPathValues.map((d, i) => {
+                return <option key={fieldKey.toString() + ':' + i} value={d.value}>{d.value}</option>;
+            });
+            const isValueSet = leafRefPathValues.filter(d => d.isSelected).length > 0;
+            if (!isValueSet || property.cardinality === '0..1') {
+                const noValueDisplayText = changeCase.title(property.name);
+                options.unshift(<option key={'(value-not-in-leafref)' + fieldKey.toString()} value="" placeholder={placeholder}>{noValueDisplayText}</option>);
+            }
+            return <select key={fieldKey.toString()} id={fieldKey.toString()} className={ClassNames({'-value-not-set': !isValueSet})} name={name} value={value} title={name} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} readOnly={!isEditable}>{options}</select>;
+        }
+
+        if (property['preserve-line-breaks']) {
+            return <textarea key={fieldKey.toString()} cols="5" id={fieldKey.toString()} name={name} value={value} placeholder={placeholder} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} onMouseOut={endEditing} onMouseLeave={endEditing} readOnly={!isEditable} />;
+        }
+
+        return <input key={fieldKey.toString()}
+                      id={fieldKey.toString()}
+                      type="text"
+                      name={name}
+                      value={fieldValue}
+                      className={className}
+                      placeholder={placeholder}
+                      onChange={onChange}
+                      onFocus={onFocus}
+                      onBlur={endEditing}
+                      onMouseDown={startEditing}
+                      onMouseOver={startEditing}
+                      onMouseOut={endEditing}
+                      onMouseLeave={endEditing}
+                      readOnly={!isEditable}
+        />;
+
+    }
+
+    function buildElement(container, property, valuePath, value) {
+        return property.properties.map((property, index) => {
+            let childValue;
+            const childPath = valuePath.slice();
+            if (typeof value === 'object') {
+                childValue = value[property.name];
+            }
+            if(property.type != 'choice'){
+                        childPath.push(property.name);
+            }
+            return build(container, property, childPath, childValue);
+
+        });
+    }
+
+    function buildChoice(container, property, path, value, key) {
+
+        function onFormFieldValueChanged(event) {
+            if (DescriptorModelFactory.isContainer(this)) {
+
+                event.preventDefault();
+
+                let name = event.target.name;
+                const value = event.target.value;
+
+
+                /*
+                    Transient State is stored for convenience in the uiState field.
+                    The choice yang type uses case elements to describe the "options".
+                    A choice can only ever have one option selected which allows
+                    the system to determine which type is selected by the name of
+                    the element contained within the field.
+                 */
+                /*
+                    const stateExample = {
+                        uiState: {
+                            choice: {
+                                'conf-config': {
+                                    selected: 'rest',
+                                    'case': {
+                                        rest: {},
+                                        netconf: {},
+                                        script: {}
+                                    }
+                                }
+                            }
+                        }
+                    };
+                */
+                const statePath = ['uiState.choice'].concat(name);
+                const stateObject = utils.resolvePath(this.model, statePath.join('.')) || {};
+                const selected = stateObject.selected ? stateObject.selected.split('.')[1] : undefined;
+                // write state back to the model so the new state objects are captured
+                utils.assignPathValue(this.model, statePath.join('.'), stateObject);
+
+                // write the current choice value into the state
+                let choiceObject = utils.resolvePath(this.model, [name, selected].join('.'));
+                let isTopCase = false;
+                if (!choiceObject) {
+                    isTopCase = true;
+                    choiceObject = utils.resolvePath(this.model, [selected].join('.'));
+                }
+                utils.assignPathValue(stateObject, [selected].join('.'), _.cloneDeep(choiceObject));
+
+                if(selected) {
+                    if(this.model.uiState.choice.hasOwnProperty(name)) {
+                        delete this.model[selected];
+                        utils.removePathValue(this.model, [name, selected].join('.'), isTopCase);
+                    } else {
+                        // remove the current choice value from the model
+                        utils.removePathValue(this.model, [name, selected].join('.'), isTopCase);
+                    }
+                }
+
+                // get any state for the new selected choice
+                const newChoiceObject = utils.resolvePath(stateObject, [value].join('.')) || {};
+
+                // assign new choice value to the model
+                if (isTopCase) {
+                    utils.assignPathValue(this.model, [name, value].join('.'), newChoiceObject);
+                } else {
+                    utils.assignPathValue(this.model, [value].join('.'), newChoiceObject)
+                }
+
+
+                // update the selected name
+                utils.assignPathValue(this.model, statePath.concat('selected').join('.'), value);
+
+                CatalogItemsActions.catalogItemDescriptorChanged(this.getRoot());
+            }
+        }
+
+        const caseByNameMap = {};
+
+        const onChange = onFormFieldValueChanged.bind(container);
+
+        const cases = property.properties.map(d => {
+            if (d.type === 'case') {
+                caseByNameMap[d.name] = d.properties[0];
+                return {
+                    optionName: d.name,
+                    optionTitle: d.description,
+                    //represents case name and case element name
+                    optionValue: [d.name, d.properties[0].name].join('.')
+                };
+            }
+            caseByNameMap[d.name] = d;
+            return {optionName: d.name};
+        });
+
+        const options = [{optionName: '', optionValue: false}].concat(cases).map((d, i) => {
+            return (
+                <option key={i} value={d.optionValue} title={d.optionTitle}>
+                    {d.optionName}
+                    {i ? null : changeCase.title(property.name)}
+                </option>
+            );
+        });
+
+        const selectName = path.join('.');
+        let selectedOptionPath = ['uiState.choice', selectName, 'selected'].join('.');
+        //Currently selected choice/case statement on UI model
+        let selectedOptionValue = utils.resolvePath(container.model, selectedOptionPath);
+        //If first time loaded, and none is selected, check if there is a value corresponding to a case statement in the container model
+        if(!selectedOptionValue) {
+            //get field properties for choice on container model
+            let fieldProperties = utils.resolvePath(container.model, selectName);
+            if(fieldProperties) {
+                //Check each case statement in model and see if it is present in container model.
+                cases.map(function(c){
+                    if(fieldProperties.hasOwnProperty(c.optionValue.split('.')[1])) {
+                        utils.assignPathValue(container.model, ['uiState.choice', selectName, 'selected'].join('.'), c.optionValue);
+                    }
+                });
+                selectedOptionValue = utils.resolvePath(container.model, ['uiState.choice', selectName, 'selected'].join('.'));
+            } else {
+                property.properties.map(function(p) {
+                    let pname = p.properties[0].name;
+                    if(container.model.hasOwnProperty(pname)) {
+                        utils.assignPathValue(container.model, ['uiState.choice', selectName, 'selected'].join('.'), [p.name, pname].join('.'));
+                    }
+                })
+                selectedOptionValue = utils.resolvePath(container.model, ['uiState.choice', selectName, 'selected'].join('.'));
+            }
+        }
+        //If selectedOptionValue is present, take first item in string which represents the case name.
+        const valueProperty = caseByNameMap[selectedOptionValue ? selectedOptionValue.split('.')[0] : undefined] || {properties: []};
+        const isLeaf = Property.isLeaf(valueProperty);
+        const hasProperties = _.isArray(valueProperty.properties) && valueProperty.properties.length;
+        const isMissingDescriptorMeta = !hasProperties && !Property.isLeaf(valueProperty);
+        //Some magic that prevents errors for arising
+        const valueResponse = valueProperty.properties.length ? valueProperty.properties.map((d, i) => {
+            const childPath = path.concat(valueProperty.name, d.name);
+            const childValue = utils.resolvePath(container.model, childPath.join('.'));
+            return (
+                <div key={childPath.concat('info', i).join(':')}>
+                    {build(container, d, childPath, childValue, props)}
+                </div>
+            );
+        }) : (!isMissingDescriptorMeta) ? build(container, valueProperty, path.concat(valueProperty.name), utils.resolvePath(container.model, path.concat(valueProperty.name).join('.')) || container.model[valueProperty.name]) : null
+        // end magic
+        const onFocus = onFocusPropertyFormInputElement.bind(container, property, path, value);
+
+        return (
+            <div key={key} className="choice">
+                <select key={Date.now()} className={ClassNames({'-value-not-set': !selectedOptionValue})} name={selectName} value={selectedOptionValue} onChange={onChange} onFocus={onFocus} onBlur={endEditing} onMouseDown={startEditing} onMouseOver={startEditing} onMouseOut={endEditing} onMouseLeave={endEditing}>
+                    {options}
+                </select>
+                {valueResponse}
+            </div>
+        );
+
+    }
+
+    function buildSimpleListItem(container, property, path, value, key, index) {
+        // todo need to abstract this better
+        const title = getTitle(value);
+        var req = require.context("../", true, /\.svg/);
+        return (
+            <div>
+                <a href="#select-list-item" key={Date.now()} className={property.name + '-list-item simple-list-item '} onClick={onClickSelectItem.bind(container, property, path, value)}>
+                    <img src={req('./' + DescriptorModelIconFactory.getUrlForType(property.name))} width="20px" />
+                    <span>{title}</span>
+                </a>
+                {buildRemovePropertyAction(container, property, path)}
+            </div>
+        );
+    }
+
+    function buildRemoveListItem(container, property, valuePath, fieldKey, index) {
+        const className = ClassNames(property.name + '-remove actions');
+        return (
+            <div key={fieldKey.concat(index).join(':')} className={className}>
+                <h3>
+                    <span className={property.type + '-name name'}>{changeCase.title(property.name)}</span>
+                    <span className="info">{index + 1}</span>
+                    {buildRemovePropertyAction(container, property, valuePath)}
+                </h3>
+            </div>
+        );
+    }
+
+    function buildLeafListItem(container, property, valuePath, value, key, index) {
+        // look at the type to determine how to parse the value
+        return (
+            <div>
+                {buildRemoveListItem(container, property, valuePath, key, index)}
+                {buildField(container, property, valuePath, value, key)}
+            </div>
+
+        );
+    }
+
+    function build(container, property, path, value, props = {}) {
+
+        const fields = [];
+        const isLeaf = Property.isLeaf(property);
+        const isArray = Property.isArray(property);
+        const isObject = Property.isObject(property);
+        const isLeafList = Property.isLeafList(property);
+        const fieldKey = [container.id].concat(path);
+        const isRequired = Property.isRequired(property);
+        const title = changeCase.titleCase(property.name);
+        const columnCount = property.properties.length || 1;
+        const isColumnar = isArray && (Math.round(props.width / columnCount) > 155);
+        const classNames = {'-is-required': isRequired, '-is-columnar': isColumnar};
+
+        if (!property.properties && isObject) {
+            const uiState = DescriptorModelMetaFactory.getModelMetaForType(property.name) || {};
+            property.properties = uiState.properties;
+        }
+
+        const hasProperties = _.isArray(property.properties) && property.properties.length;
+        const isMissingDescriptorMeta = !hasProperties && !Property.isLeaf(property);
+
+        // ensure value is not undefined for non-leaf property types
+        if (isObject) {
+            if (typeof value !== 'object') {
+                value = isArray ? [] : {};
+            }
+        }
+        const valueAsArray = _.isArray(value) ? value : isLeafList && typeof value === 'undefined' ? [] : [value];
+
+        const isMetaField = property.name === 'meta';
+        const isCVNFD = property.name === 'constituent-vnfd';
+        const isSimpleListView = Property.isSimpleList(property);
+
+        valueAsArray.forEach((value, index) => {
+
+            let field;
+            const key = fieldKey.slice();
+            const valuePath = path.slice();
+
+            if (isArray) {
+                valuePath.push(index);
+                key.push(index);
+            }
+
+            if (isMetaField) {
+                if (typeof value === 'object') {
+                    value = JSON.stringify(value, undefined, 12);
+                } else if (typeof value !== 'string') {
+                    value = '{}';
+                }
+            }
+
+            if (isMissingDescriptorMeta) {
+                field = <span key={key.concat('warning').join(':')} className="warning">No Descriptor Meta for {property.name}</span>;
+            } else if (property.type === 'choice') {
+                field = buildChoice(container, property, valuePath, value, key.join(':'));
+            } else if (isSimpleListView) {
+                field = buildSimpleListItem(container, property, valuePath, value, key, index);
+            } else if (isLeafList) {
+                field = buildLeafListItem(container, property, valuePath, value, key, index);
+            } else if (hasProperties) {
+                field = buildElement(container, property, valuePath, value, key.join(':'))
+            } else {
+                field = buildField(container, property, valuePath, value, key.join(':'));
+            }
+
+            function onClickLeaf(property, path, value, event) {
+                if (event.isDefaultPrevented()) {
+                    return;
+                }
+                event.preventDefault();
+                event.stopPropagation();
+                this.getRoot().uiState.focusedPropertyPath = path.join('.');
+                console.log('property selected', path.join('.'));
+                ComposerAppActions.propertySelected([path.join('.')]);
+            }
+
+            const clickHandler = isLeaf ? onClickLeaf : () => {};
+            const isContainerList = isArray && !(isSimpleListView || isLeafList);
+
+            fields.push(
+                <div key={fieldKey.concat(['property-content', index]).join(':')}
+                     className={ClassNames('property-content', {'simple-list': isSimpleListView})}
+                     onClick={clickHandler.bind(container, property, valuePath, value)}>
+                    {isContainerList ? buildRemoveListItem(container, property, valuePath, fieldKey, index) : null}
+                    {field}
+                </div>
+            );
+
+        });
+
+        classNames['-is-leaf'] = isLeaf;
+        classNames['-is-array'] = isArray;
+        classNames['cols-' + columnCount] = isColumnar;
+
+        if (property.type === 'choice') {
+            value = utils.resolvePath(container.model, ['uiState.choice'].concat(path, 'selected').join('.'));
+            if(!value) {
+                property.properties.map(function(p) {
+                    let pname = p.properties[0].name;
+                    if(container.model.hasOwnProperty(pname)) {
+                        value = container.model[pname];
+                    }
+                })
+            }
+        }
+
+        let displayValue = typeof value === 'object' ? '' : value;
+        const displayValueInfo = isArray ? valueAsArray.filter(d => typeof d !== 'undefined').length + ' items' : '';
+
+        const onFocus = isLeaf ? event => event.target.classList.add('-is-focused') : false;
+
+        return (
+            <div key={fieldKey.join(':')} className={ClassNames(property.type + '-property property', classNames)} onFocus={onFocus}>
+                <h3 className="property-label">
+                    <label htmlFor={fieldKey.join(':')}>
+                        <span className={property.type + '-name name'}>{title}</span>
+                        <small>
+                            <span className={property.type + '-info info'}>{displayValueInfo}</span>
+                            <span className={property.type + '-value value'}>{displayValue}</span>
+                        </small>
+                        {isArray ? buildAddPropertyAction(container, property, path.concat(valueAsArray.length)) : null}
+                    </label>
+                </h3>
+                <span className={property.type + '-description description'}>{property.description}</span>
+                <val className="property-value">
+                    {isCVNFD ? <span className={property.type + '-tip tip'}>Drag a VNFD from the Catalog to add more.</span> : null}
+                    {fields}
+                </val>
+            </div>
+        );
+
+    }
+
+    const containerType = container.uiState['qualified-type'] || container.uiState.type;
+    const basicProperties = getDescriptorMetaBasicForType(containerType).properties;
+
+    function buildBasicGroup() {
+        if (basicProperties.length === 0) {
+            return null;
+        }
+        return (
+            <div className="basic-properties-group">
+                <h2>Basic</h2>
+                <div>
+                    {basicProperties.map(property => {
+                        const path = [property.name];
+                        const value = container.model[property.name];
+                        return build(container, property, path, value);
+                    })}
+                </div>
+            </div>
+        );
+    }
+
+    function buildAdvancedGroup() {
+        const properties = getDescriptorMetaAdvancedForType(containerType).properties;
+        if (properties.length === 0) {
+            return null;
+        }
+        const hasBasicFields = basicProperties.length > 0;
+        const closeGroup = basicProperties.length > 0;
+        return (
+            <div className="advanced-properties-group">
+                <h1 data-toggle={closeGroup ? 'true' : 'false'} className={ClassNames({'-is-toggled': closeGroup})} onClick={toggle} style={{display: hasBasicFields ? 'block' : 'none'}}>
+                    <a className="toggle-show-more" href="#show-more-properties">more&hellip;</a>
+                    <a className="toggle-show-less" href="#show-more-properties">less&hellip;</a>
+                </h1>
+                <div className="toggleable">
+                    {properties.map(property => {
+                        const path = [property.name];
+                        const value = container.model[property.name];
+                        return build(container, property, path, value, {toggle: true, width: props.width});
+                    })}
+                </div>
+                <div className="toggle-bottom-spacer" style={{visibility: 'hidden', 'height': '50%', position: 'absolute'}}>We need this so when the user closes the panel it won't shift away and scare the bj out of them!</div>
+            </div>
+        );
+    }
+
+    function buildMoreLess(d, i) {
+        return (
+            <span key={'bread-crumb-part-' + i}>
+                <a href="#select-item" onClick={onClickSelectItem.bind(d, null, null, d)}>{d.title}</a>
+                <i> / </i>
+            </span>
+        );
+    }
+
+    const path = [];
+    if (container.parent) {
+        path.push(container.parent);
+    }
+    path.push(container);
+
+    return (
+        <div className="EditDescriptorModelProperties -is-tree-view">
+            <h1>{path.map(buildMoreLess)}</h1>
+            {buildBasicGroup()}
+            {buildAdvancedGroup()}
+        </div>
+    );
+
+}
+// export buildElement;
+// export buildChoice;
index 1a2ba4f..b0acc08 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
index ac59872..d5b6a28 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -51,7 +51,7 @@ import VirtualNetworkFunctionReadOnlyWrapper from './descriptors/VirtualNetworkF
 import InternalConnectionPointRef from './descriptors/InternalConnectionPointRef'
 import VirtualNetworkFunctionConnectionPoint from './descriptors/VirtualNetworkFunctionConnectionPoint'
 import VirtualDeploymentUnitInternalConnectionPoint from './descriptors/VirtualDeploymentUnitInternalConnectionPoint'
-
+import VirtualNetworkFunctionAccessPointMap from './descriptors/VirtualNetworkFunctionAccessPointMap'
 function findChildDescriptorModelAndUpdateModel(model, parent) {
        if (parent instanceof DescriptorModel) {
                const child = parent.findChildByUid(model);
@@ -120,6 +120,9 @@ class DescriptorModelFactory {
                        fg.rsp.forEach(rsp => mapRSP(rsp, containerList));
                        fg.classifier.forEach(classifier => mapClassifier(classifier, containerList));
                }
+        function mapConfigParameterMap(ap, containerList) {
+            containerList.push(ap);
+        }
 
                function mapVDU(vdu, containerList) {
                        containerList.push(vdu);
@@ -143,6 +146,7 @@ class DescriptorModelFactory {
                        nsd.constituentVnfd.forEach(cvnfd => mapCVNFD(cvnfd, containerList));
                        nsd.vld.forEach(vld => mapVLD(vld, containerList));
                        nsd.vnffgd.forEach(fg => mapFG(fg, containerList));
+            nsd.configParameterMap.forEach(ap => mapConfigParameterMap(ap, containerList));
                }
 
                function mapVNFD(vnfd, containerList) {
@@ -226,6 +230,9 @@ class DescriptorModelFactory {
                return findChildDescriptorModelAndUpdateModel(model, parent) || new VnfdConnectionPointRef(model, parent);
        }
 
+    static newVirtualNetworkFunctionAccessPointMap(model, parent) {
+        return findChildDescriptorModelAndUpdateModel(model, parent) || new VirtualNetworkFunctionAccessPointMap(model, parent);
+    }
        static newForwardingGraph(model, parent) {
                return findChildDescriptorModelAndUpdateModel(model, parent) || new ForwardingGraph(model, parent);
        }
@@ -322,6 +329,9 @@ class DescriptorModelFactory {
                return obj instanceof VirtualNetworkFunction;
        }
 
+    static isVirtualNetworkFunctionAccessPointMap(obj) {
+        return obj instanceof VirtualNetworkFunctionAccessPointMap;
+    }
        static isForwardingGraph(obj) {
                return obj instanceof ForwardingGraph;
        }
@@ -342,6 +352,9 @@ class DescriptorModelFactory {
                return NetworkService;
        }
 
+    static get VirtualNetworkFunctionAccessPointMap () {
+        return VirtualNetworkFunctionAccessPointMap;
+    }
        static get ForwardingGraph () {
                return ForwardingGraph;
        }
index 8b86b0c..d8d9e58 100644 (file)
@@ -28,6 +28,7 @@ export default {
        vld: common.concat([]),
        vnfd: common.concat(['vdu', 'internal-vld']),
        'vnfd.vdu': common.concat(['image', 'image-checksum', 'external-interface', 'vm-flavor', 'cloud-init', 'filename']),
+    'nsd.config-parameter-map': common.concat([]),
        // white-list valid fields to send in the meta field
        meta: ['containerPositionMap']
 };
index d3ef200..b8624ec 100644 (file)
@@ -14,6 +14,7 @@ const assign = Object.assign;
 
 const exportInnerTypesMap = {
        'constituent-vnfd': 'nsd.constituent-vnfd',
+    'config-parameter-map': 'nsd.config-parameter-map',
        'vdu': 'vnfd.vdu'
 };
 
index 89d1fe9..5b066cf 100644 (file)
@@ -136,6 +136,31 @@ export default {
 
                return leafRefObjects;
        },
+
+       getConfigParamRef(property = {}, path, value, fullFieldKey, transientCatalogs, container, vnfdId) {
+               // const leafRefPath = property['data-type']['leafref']['path'];
+               const leafRefPath = "/vnfd:vnfd-catalog/vnfd:vnfd[vnfd:id = " + vnfdId + "]/vnfd:config-parameter/vnfd:config-parameter-source/vnfd:name"
+               const transientCatalogHash = {};
+
+               transientCatalogs.map((catalog) => {
+                       transientCatalogHash[catalog.type + '-catalog'] = {};
+                       transientCatalogHash[catalog.type + '-catalog'][catalog.type] = catalog['descriptors'];
+               });
+
+               let leafRefPathValues = utils.resolveLeafRefPath(transientCatalogHash, leafRefPath, fullFieldKey, path, container);
+
+               let leafRefObjects = [];
+
+               leafRefPathValues && leafRefPathValues.map((leafRefPathValue) => {
+                       leafRefObjects.push({
+                               name: leafRefPathValue,
+                               value: leafRefPathValue,
+                               isSelected: String(leafRefPathValue) === String(value)
+                       });
+               });
+
+               return leafRefObjects;
+       },
        isGuid(property = {}) {
                const type = property['data-type'];
                if (typeof type === 'object' && type.leafref && type.leafref.path) {
index 8ce90cf..449f0a3 100644 (file)
@@ -29,6 +29,7 @@ let nsdFields = null;
 let vldFields = null;
 let vnfdFields = null;
 let cvnfdFields = null;
+let configParameterMapFields = null;
 
 
 
@@ -197,6 +198,13 @@ const DescriptorModelSerializer = {
                        const confd = _.omit(copy, ['uiState']);
                        return cleanEmptyTopKeys(confd);
                }
+    },
+    'config-parameter-map': {
+        serialize(configParameterMap) {
+            //vnfapMapFields
+            if(!configParameterMapFields) configParameterMapFields = DescriptorModelMetaFactory.getModelFieldNamesForType('nsd.config-parameter-map');
+            return _.pick(configParameterMap, configParameterMapFields);
+        }
        }
 };
 
index 16fb159..494bf38 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,6 +25,7 @@ import _ from 'lodash'
 import ColorGroups from '../../ColorGroups'
 import DescriptorModel from '../DescriptorModel'
 import ForwardingGraph from './ForwardingGraph'
+import VirtualNetworkFunctionAccessPointMap from './VirtualNetworkFunctionAccessPointMap'
 import VirtualLink from './VirtualLink'
 import ConstituentVnfd from './ConstituentVnfd'
 import PhysicalNetworkFunction from './PhysicalNetworkFunction'
@@ -128,6 +129,45 @@ export default class NetworkService extends DescriptorModel {
        }
 
 
+// <<<<<<< Updated upstream
+//     get configParameterMap() {
+//         if (!this.model['config-parameter-map']) {
+//             this.model['config-parameter-map'] = [];
+//         }
+//         return this.model['config-parameter-map'].map(d => DescriptorModelFactory.newVirtualNetworkFunctionAccessPointMap(d, this)).map((fg, i) => {
+//             return fg;
+//         });
+//     }
+
+//     set configParameterMap(obj) {
+//         const onVirtualNetworkFunctionAccessPointMap = (fg) => {
+
+//         };
+//         this.updateModelList('config-parameter-map', obj, VirtualNetworkFunctionAccessPointMap, onVirtualNetworkFunctionAccessPointMap);
+//     }
+
+//     createConfigParameterMap(model) {
+//         model = model || DescriptorModelMetaFactory.createModelInstanceForType('nsd.config-parameter-map');
+//         return this.configParameterMap = DescriptorModelFactory.newVirtualNetworkFunctionAccessPointMap(model, this);
+//     }
+// =======
+       get configParameterMap() {
+               if (!this.model['config-parameter-map']) {
+                       this.model['config-parameter-map'] = [];
+               }
+               return this.model['config-parameter-map'].map(d => DescriptorModelFactory.newVirtualNetworkFunctionAccessPointMap(d, this))
+       }
+
+       set configParameterMap(obj) {
+               this.updateModelList('config-parameter-map', obj, VirtualNetworkFunctionAccessPointMap);
+       }
+
+       createConfigParameterMap() {
+               const model = DescriptorModelMetaFactory.createModelInstanceForType('nsd.config-parameter-map');
+               return this.configParameterMap = DescriptorModelFactory.newVirtualNetworkFunctionAccessPointMap(model, this);
+       }
+// >>>>>>> Stashed changes
+
        get vnffgd() {
                if (!this.model.vnffgd) {
                        this.model.vnffgd = [];
@@ -160,6 +200,7 @@ export default class NetworkService extends DescriptorModel {
        }
 
 
+
        // NOTE temporarily disable NSD connection points
        // https://trello.com/c/crVgg2A1/88-do-not-render-nsd-connection-in-the-composer
        //get connectionPoint() {
diff --git a/skyquake/plugins/composer/src/src/libraries/model/descriptors/VirtualNetworkFunctionAccessPoint.js b/skyquake/plugins/composer/src/src/libraries/model/descriptors/VirtualNetworkFunctionAccessPoint.js
new file mode 100644 (file)
index 0000000..defa317
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ *
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   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.
+ *
+ */
+/**
+ * Created by onvelocity on 11/23/15.
+ */
+
+'use strict';
+
+import DescriptorModel from '../DescriptorModel'
+import DescriptorModelFactory from '../DescriptorModelFactory'
+
+export default class VnfapMap extends DescriptorModel {
+
+    static get type() {
+        return 'vnfap-map';
+    }
+
+    static get className() {
+        return 'VnfapMap';
+    }
+
+    constructor(model, parent) {
+        super(model, parent);
+        this.type = 'vnfap-map';
+        this.uiState['qualified-type'] = 'nsd.vnfap-map';
+        this.className = 'VnfapMap';
+        // this.addProp('vnfdRef', DescriptorModelFactory.newVirtualNetworkFunctionReadOnlyWrapper({}, this));
+    }
+
+    get id() {
+        return this.model.id;
+    }
+    get capability() {
+        return []
+    }
+
+    get dependency() {
+
+    }
+
+
+}
diff --git a/skyquake/plugins/composer/src/src/libraries/model/descriptors/VirtualNetworkFunctionAccessPointMap.js b/skyquake/plugins/composer/src/src/libraries/model/descriptors/VirtualNetworkFunctionAccessPointMap.js
new file mode 100644 (file)
index 0000000..16b57dc
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ *
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   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.
+ *
+ */
+/**
+ * Created by onvelocity on 11/23/15.
+ */
+
+'use strict';
+
+import DescriptorModel from '../DescriptorModel'
+import DescriptorModelFactory from '../DescriptorModelFactory'
+
+export default class ConfigParameterMap extends DescriptorModel {
+
+// <<<<<<< Updated upstream
+//     static get type() {
+//         return 'config-parameter-map';
+//     }
+
+//     static get className() {
+//         return 'ConfigParameterMap';
+//     }
+
+//     constructor(model, parent) {
+//         super(model, parent);
+//         this.type = 'config-parameter-map';
+//         this.uiState['qualified-type'] = 'nsd.config-parameter-map';
+//         this.className = 'ConfigParameterMap';
+//         // this.addProp('vnfdRef', DescriptorModelFactory.newVirtualNetworkFunctionReadOnlyWrapper({}, this));
+//     }
+
+//     get id() {
+//         return this.model.id;
+//     }
+//     get capability() {
+//         return {
+//             'member-vnf-index': this.model.capability['member-vnf-index'],
+//             'capability-ref': this.model.capability['capability-ref'],
+//         }
+//     }
+//     get availableCapabilities() {
+
+//     }
+
+//     get dependency() {
+
+//     }
+// =======
+       static get type() {
+               return 'config-parameter-map';
+       }
+
+       static get className() {
+               return 'ConfigParameterMap';
+       }
+       static get qualifiedType() {
+               return 'nsd.' + ConfigParameterMap.type;
+       }
+       constructor(model, parent) {
+               super(model, parent);
+               this.type = 'config-parameter-map';
+               this.uiState['qualified-type'] = 'nsd.config-parameter-map';
+               this.className = 'ConfigParameterMap';
+               // this.addProp('vnfdRef', DescriptorModelFactory.newVirtualNetworkFunctionReadOnlyWrapper({}, this));
+       }
+
+       get id() {
+               return this.model.id;
+       }
+       // get capability() {
+       //      return {
+       //              'member-vnf-index': this.model.capability['member-vnf-index'],
+       //              'capability-ref': this.model.capability['capability-ref'],
+       //      }
+       // }
+       // get availableCapabilities() {
+
+       // }
+
+       // get dependency() {
+
+       // }
+
+
+}
index 1d54a6f..d2a7535 100644 (file)
@@ -271,11 +271,11 @@ export default {
 
                // Check if relative path or not
                // TODO: Below works but
-               // better to convert the pathCopy to absolute/rooted path 
+               // better to convert the pathCopy to absolute/rooted path
                // and use the absolute module instead
                if (this.isRelativePath(leafRefPathCopy)) {
                        let i = pathArray.length;
-                       while (pathArray[pathArray.length - i] == '..') {
+                       while ((pathArray[pathArray.length - i] == '..') && fieldKeyArray.length > 1) {
                                fieldKeyArray.splice(-1, 1);
                                if (!isNaN(Number(fieldKeyArray[fieldKeyArray.length - 1]))) {
                                        // found a number, so an index. strip it
@@ -310,7 +310,7 @@ export default {
                                                }
                                        }
                                }
-                       } else {
+                       }  else {
                                // not supported - too many levels deep ... maybe some day
                                console.log('The relative path is from a node too many levels deep from root. This is not supported at the time');
                        }
index 3671890..b30193a 100644 (file)
@@ -341,13 +341,21 @@ class ComposerAppStore {
                this.setState({showJSONViewer: false});
        }
 
-       toggleCanvasPanelTray() {
+       toggleCanvasPanelTray(event) {
                const layout = this.layout;
-               if (layout.bottom > 25) {
+               const attrMap = event.target.attributes;
+               let panelEvent = null;
+               for(let k in attrMap) {
+                       if(attrMap[k].name == 'data-event') {
+                               panelEvent = attrMap[k].nodeValue;
+                       }
+               }
+               if ((layout.bottom > 25) && ((panelEvent == this.displayedPanel) || panelEvent == 'arrow')) {
                        this.closeCanvasPanelTray();
                } else {
                        this.openCanvasPanelTray();
                }
+               this.setState({displayedPanel: panelEvent})
        }
 
        openCanvasPanelTray() {
index 9fa1149..2f684d2 100644 (file)
@@ -28,6 +28,7 @@
        min-width: 300px;
        overflow: hidden;
        z-index: 1;
+
        .CanvasPanelHeader {
                h1 {
                        margin: 0;
index 7e9e336..94f2b43 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,7 +29,8 @@ $tray-head-height: 25px;
        right: 0;
        height: 25px;
        min-width: 300px;
-       background-color: white;
+       /* background-color: white;*/
+       background: $panel-bg-color;
        &.-with-transitions {
                transition: height 300ms cubic-bezier(0.230, 1.000, 0.320, 1.000);
        }
@@ -60,4 +61,43 @@ $tray-head-height: 25px;
                }
        }
 
+
+       &-buttons {
+               display: -ms-flexbox;
+               display: flex;
+               margin-top: 2px;
+               button {
+                       padding: 6px 34px;
+               }
+       }
+       .tray-body {
+               top:31px;
+       }
+       .ConfigParameterMap {
+               padding: 10px 20px;
+               background: $panel-bg-color;
+
+               .toggleable {
+                       display:-ms-flexbox;
+                       display:flex;
+                       -ms-flex-wrap: wrap;
+                           flex-wrap: wrap;
+                   background: $panel-bg-color-contrast;
+                   margin:8px 0;
+                       & > .leaf-property {
+                           -ms-flex: 1 0 100%;
+                       flex: 1 0 100%;
+                       margin: 8px 0;
+                               padding: 0 8px;
+                               background: none;
+                       .property-label {
+                       }
+                       }
+                       & > .container-property {
+                               -ms-flex: 1 1;
+                                   flex: 1 1;
+                       }
+               }
+       }
+
 }
diff --git a/skyquake/plugins/composer/src/src/styles/EditConfigParameterMap.scss b/skyquake/plugins/composer/src/src/styles/EditConfigParameterMap.scss
new file mode 100644 (file)
index 0000000..352fa95
--- /dev/null
@@ -0,0 +1,357 @@
+/*
+ *
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   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 "main";
+@import "variables";
+@import "ColorGroups";
+
+.config-parameter-map {
+
+       $field-border-radius: 3px;
+       $field-background-color: white;
+       $child-indent-left-right-margin: 5px;
+
+       font-size: smaller;
+       display:-ms-flexbox;
+       display:flex;
+       -ms-flex-wrap: wrap;
+           flex-wrap: wrap;
+
+
+
+       max-width: 980px;
+       .config-parameter-titles {
+               display: -ms-flexbox;
+               display: flex;
+               -ms-flex: 1 1 100%;
+                   flex: 1 1 100%;
+           padding-bottom:10px;
+           color: #AEAEAE;
+           text-transform:uppercase;
+                   .config-parameter {
+                       padding-left: 10px;
+                   }
+       }
+       .config-parameter {
+               display:-ms-flexbox;
+               display:flex;
+               -ms-flex: 1 1 50%;
+                   flex: 1 1 50%;
+                   &-source, &-request {
+                       -ms-flex: 1 1 50%;
+                       flex: 1 1 50%;
+                               border-top:1px solid silver;
+                               padding: 10px 10px;
+                                                   }
+                   &-request{
+                       line-height: 25px;
+                   }
+                   &-group {
+                       -ms-flex: 1 1 100%;
+                           flex: 1 1 100%;
+                       display:-ms-flexbox;
+                       display:flex;
+                       &:nth-child(2) {
+                               background: $panel-bg-color-contrast;
+                       }
+                   }
+       }
+
+
+       h1 {
+               text-align: left;
+               span:last-child {
+                       i {
+                               display: none;
+                       }
+               }
+       }
+
+       h2 {
+               @extend h1;
+
+       }
+
+       .basic-properties-group {
+               > h1 {
+                       display: none;
+               }
+       }
+
+       .advanced-properties-group {
+       }
+
+       /* label is used as list item headers */
+       h3 {
+               @extend h2;
+               display: inline-block;
+               .name {
+                       color: #586e75;
+               }
+               .value {
+                       display: none;
+                       color: #002b36;
+               }
+               .info {
+                       margin: 4px;
+               }
+       }
+
+       val {
+               display: inline-block;
+               color: #073642;
+       }
+
+       a {
+               &.simple-list-item {
+                       display: inline-block;
+                       width: 230px;
+                       padding: 4px 16px 4px 4px;
+                       white-space: nowrap;
+               }
+               &.vld-list-item,
+               &.internal-vld-list-item{
+                       border: 1px solid $vld-primary-color;
+                       border-radius: 24px;
+                       background: white linear-gradient(to right, $vld-primary-color 34px, white 34px);;
+               }
+               &.vnfd-list-item,
+               &.constituent-vnfd-list-item{
+                       border: 1px solid $vnfd-primary-color;
+                       border-radius: 11px;
+                       background: white linear-gradient(to right, $vnfd-primary-color 34px, white 34px);;
+               }
+               &.vdu-list-item {
+                       border: 1px solid $vdu-primary-color;
+                       border-radius: 11px;
+                       background: white linear-gradient(to right, $vdu-primary-color 34px, white 34px);;
+               }
+               &.vnffgd-list-item {
+                       border: 1px solid $vnffgd-primary-color;
+                       border-radius: 11px;
+                       background: white linear-gradient(to right, $vnffgd-primary-color 34px, white 34px);;
+               }
+       }
+
+       &.-is-tree-view {
+               .property {
+
+                       position: relative;
+                       overflow: hidden;
+
+                       margin: 8px 8px;
+
+                       background-color: rgba(147, 161, 161, 0.5);
+                       border-radius: $field-border-radius;
+
+                       > h3 {
+                               position: absolute;
+                               top: 2px;
+                               right: 18px;
+                               height: 21px;
+                               pointer-events: none;
+                               border-radius: $field-border-radius;
+                               padding: 3px 0;
+                       }
+
+                       > val {
+                               width: 100%;
+                               > .property-content {
+                                       width: 100%;
+                               }
+
+                       }
+
+                       &.-is-focused {
+                               > h3 {
+                                       /*z-index: -1;*/
+                               }
+                       }
+
+                       &.leaf-property {
+                               overflow: hidden;
+                               min-height: 25px;
+                               > h3 {
+                                       background: linear-gradient(to right, transparent, $field-background-color 21px);
+                                       padding-left: 25px;
+                               }
+                               > val {
+                                       border-radius: $field-border-radius;
+                                       > .property-content {
+                                               border-radius: $field-border-radius;
+                                       }
+                               }
+                       }
+
+                       &.property:not(.leaf-property) {
+
+                               padding: 7px $child-indent-left-right-margin 0 $child-indent-left-right-margin;
+
+                               &.list-property {
+                                       > h3 {
+                                               padding: 4px 8px;
+                                       }
+                               }
+
+                               > h3 {
+                                       right: auto;
+                                       top: 0;
+                                       left: 0;
+                                       width: 100%;
+                                       height: 25px;
+                                       z-index: 1;
+                                       padding: 8px;
+                                       /*background: red;*/
+                                       pointer-events: all;
+                               }
+                               > val {
+                                       margin: 28px 0 8px 0;
+                                       > .property-content {
+                                               position: relative;
+                                               border-radius: $field-border-radius;
+                                               margin-top: 4px;
+                                               padding: 4px;
+                                               &:first-of-type {
+                                                       margin-top: 0;
+                                               }
+                                               &.simple-list {
+                                                       min-height: 15px;
+                                                       margin: 11px;
+                                                       .simple-list-item {
+                                                               img,
+                                                               span {
+                                                                       margin: 0 5px;
+                                                                       vertical-align: middle;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                                       > .property-content:not(.simple-list) {
+                                               &:nth-of-type(odd) {
+                                                       background-color: rgba(238, 232, 213, 0.33);
+                                               }
+                                               &:nth-of-type(even) {
+                                                       background-color: rgba(147, 161, 161, 0.33);
+                                               }
+                                       }
+                                       .tip {
+                                               font-style: italic;
+                                               font-size: small;
+                                               color: #93a1a1;
+                                       }
+                               }
+                       }
+
+                       .actions {
+                               span {
+                                       vertical-align: middle;
+                                       padding: 0 5px;
+                               }
+                       }
+
+               }
+
+       }
+
+       .description {
+               display: none;
+       }
+
+       input,
+       select,
+       textarea {
+               height: 25px;
+               line-height: 25px;
+               max-width: 100%;
+               min-width: 100%;
+               margin: 0;
+               padding: 0 0px 4px 8px;
+               border: 1px solid $field-background-color;
+               border-radius: $field-border-radius;
+               color: #002b36;
+               background-color: $field-background-color;
+               vertical-align: top;
+               &:focus {
+                       color: #002b36;
+                       background-color: white !important;
+               }
+               &::-webkit-input-placeholder {
+                       color: #eee8d5 !important;
+               }
+
+               &:-moz-placeholder { /* Firefox 18- */
+                       color: #eee8d5 !important;
+               }
+
+               &::-moz-placeholder {  /* Firefox 19+ */
+                       color: #eee8d5 !important;
+               }
+
+               &:-ms-input-placeholder {
+                       color: #eee8d5 !important;
+               }
+       }
+
+       select {
+               padding-right: 0;
+               margin-right: 0;
+               -webkit-appearance: none;
+               -webkit-border-radius: $field-border-radius;
+               &.-value-not-set {
+                       color: #eee8d5;
+               }
+       }
+
+       select {
+               -webkit-appearance: none;
+                  -moz-appearance: none;
+                       appearance: none; /* using -prefix-free http://leaverou.github.io/prefixfree/*/
+        background: $field-background-color url(../../../node_modules/open-iconic/svg/caret-bottom.svg) no-repeat right 8px center;
+               background-size: 10px;
+               border: {
+                       color: $field-background-color;
+                       radius: $field-border-radius;
+                       style: solid;
+                       width: 1px;
+               }
+       }
+
+       /* Removes default arrow for IE10+*/
+       /* IE 8/9 get dafault arrow which covers caret image*/
+       /* as long as caret image is small than and positioned*/
+       /* behind default arrow*/
+       select::-ms-expand {
+               display: none;
+       }
+
+       textarea {
+               height: 50px;
+       }
+
+       input {
+               padding: 0 20px 0 8px;
+               line-height: 25px;
+       }
+
+       input[name$="id"],
+       input.-is-guid {
+               font-size: 10px;
+               font-family: monospace;
+       }
+
+}