RIFT-14803: UI Composer: References should be dropdowns https://osm.etsi.org/gerrit... 42/842/1
authorKIRAN KASHALKAR <kiran.kashalkar@riftio.com>
Tue, 27 Dec 2016 02:16:09 +0000 (21:16 -0500)
committerKIRAN KASHALKAR <kiran.kashalkar@riftio.com>
Tue, 27 Dec 2016 02:16:09 +0000 (21:16 -0500)
Signed-off-by: KIRAN KASHALKAR <kiran.kashalkar@riftio.com>
skyquake/plugins/composer/src/src/components/EditDescriptorModelProperties.js
skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaProperty.js
skyquake/plugins/composer/src/src/libraries/utils.js
skyquake/plugins/composer/src/src/stores/CatalogDataStore.js

index 4dc4b28..7965e25 100644 (file)
@@ -39,6 +39,7 @@ 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'
@@ -173,12 +174,15 @@ export default function EditDescriptorModelProperties(props) {
        }
 
        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});
@@ -200,6 +204,26 @@ export default function EditDescriptorModelProperties(props) {
                        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} />;
                }
index 67f34e2..8ba295a 100644 (file)
@@ -29,6 +29,7 @@ import changeCase from 'change-case'
 import InstanceCounter from './../InstanceCounter'
 import DescriptorModelFields from './DescriptorModelFields'
 import DescriptorTemplateFactory from './DescriptorTemplateFactory'
+import utils from '../utils'
 
 export default {
        isLeaf(property = {}) {
@@ -40,6 +41,10 @@ export default {
        isLeafList(property = {}) {
         return property.type === 'leaf_list';
        },
+       isLeafRef(property = {}) {
+               const type = property['data-type'] || {};
+               return type.hasOwnProperty('leafref');
+       },
        isArray(property = {}) {
                // give '1' or '0..N' or '0..1' or '0..5' determine if represents an array
                // '0..1' is not an array
@@ -107,6 +112,30 @@ export default {
                        return {name: enumName, value: enumValue, isSelected: String(enumValue) === String(value)};
                });
        },
+       getLeafRef(property = {}, path, value, fullFieldKey, transientCatalogs, container) {
+               const leafRefPath = property['data-type']['leafref']['path'];
+
+               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 ec69bc4..a182b1a 100644 (file)
@@ -123,6 +123,211 @@ export default {
                };
        },
 
-       toBiggestValue: (maxIndex, curIndex) => Math.max(maxIndex, curIndex)
+       toBiggestValue: (maxIndex, curIndex) => Math.max(maxIndex, curIndex),
 
+       isRelativePath (path) {
+               if (path.split('/')[0] == '..') {
+                       return true;
+               }
+               return false;
+       },
+
+       getResults (topLevelObject, pathArray) {
+               let objectCopy = _.cloneDeep(topLevelObject);
+               let i = pathArray.length;
+               let results = [];
+
+               while(pathArray[pathArray.length - i]) {
+                       if (_.isArray(objectCopy[pathArray[pathArray.length - i]])) {
+                               if (i == 2) {
+                                       results = _.map(objectCopy[pathArray[pathArray.length - i]], pathArray[pathArray.length - 1]);
+                               } else {
+                                       objectCopy = objectCopy[pathArray[pathArray.length - i]];
+                               }
+                       } else if (_.isArray(objectCopy)) {
+                               objectCopy.map((object) => {
+                                       if (_.isArray(object[pathArray[pathArray.length - i]])) {
+                                               if (i == 2) {
+                                                       results = results.concat(_.map(object[pathArray[pathArray.length - i]], pathArray[pathArray.length - 1]));
+                                               }
+                                       }
+                               })
+                       }
+                       i--;
+               }
+
+               return results;
+       },
+
+       getAbsoluteResults (topLevelObject, pathArray) {
+               let i = pathArray.length;
+               let objectCopy = _.cloneDeep(topLevelObject);
+               let results = [];
+
+               let fragment = pathArray[pathArray.length - i]
+
+               while (fragment) {
+                       if (i == 1) {
+                               // last fragment
+                               if (_.isArray(objectCopy)) {
+                                       // results will be obtained from a map
+                                       results = _.map(objectCopy, fragment);
+                               } else {
+                                       // object
+                                       if (fragment.match(/\[.*\]/g)) {
+                                               // contains a predicate
+                                               // shouldn't reach here
+                                               console.log('Something went wrong while resolving a leafref. Reached a leaf with predicate.');
+                                       } else {
+                                               // contains no predicate
+                                               results.push(objectCopy[fragment]);
+                                       }
+                               }
+                       } else {
+                               if (_.isArray(objectCopy)) {
+                                       // is array
+                                       objectCopy = _.map(objectCopy, fragment);
+
+                                       // If any of the deeper object is an array, flatten the entire list.
+                                       // This would usually be a bad leafref going out of its scope.
+                                       // Log it too
+                                       for (let i = 0; i < objectCopy.length; i++) {
+                                               if (_.isArray(objectCopy[i])) {
+                                                       objectCopy = _.flatten(objectCopy);
+                                                       console.log('This might be a bad leafref. Verify with backend team.')
+                                                       break;
+                                               }
+                                       }
+                               } else {
+                                       // is object
+                                       if (fragment.match(/\[.*\]/g)) {
+                                               // contains a predicate
+                                               let predicateStr = fragment.match(/\[.*\]/g)[0];
+                                               // Clip leading [ and trailing ]
+                                               predicateStr = predicateStr.substr(1, predicateStr.length - 2);
+                                               const predicateKeyValue = predicateStr.split('=');
+                                               const predicateKey = predicateKeyValue[0];
+                                               const predicateValue = predicateKeyValue[1];
+                                               // get key for object to search into
+                                               let key = fragment.split('[')[0];
+                                               let searchObject = {};
+                                               searchObject[predicateKey] = predicateValue;
+                                               objectCopy = _.find(objectCopy[key], searchObject);
+                                               if (!objectCopy) {
+                                                       return [];
+                                               }
+                                       } else {
+                                               // contains no predicate
+                                               objectCopy = objectCopy[fragment];
+                                       }
+                               }
+                       }
+                       i--;
+                       fragment = pathArray[pathArray.length - i];
+               }
+
+               return results;
+       },
+
+       resolveCurrentPredicate (leafRefPath, container, pathCopy) {
+               if (leafRefPath.indexOf('current()') != -1) {
+                       // contains current
+
+                       // Get the relative path from current
+                       let relativePath = leafRefPath.match("current\\(\\)\/(.*)\]");
+                       let relativePathArray = relativePath[1].split('/');
+
+                       while (relativePathArray[0] == '..') {
+                               pathCopy.pop();
+                               relativePathArray.shift();
+                       }
+
+                       // Supports only one relative path up
+                       // To support deeper paths, will need to massage the string more
+                       // i.e. change '/'' to '.'
+                       const searchPath = pathCopy.join('.').concat('.', relativePathArray[0]);
+                       const searchValue = this.resolvePath(container.model, searchPath);
+
+                       const predicateStr = leafRefPath.match("(current.*)\]")[1];
+                       leafRefPath = leafRefPath.replace(predicateStr, searchValue);
+               }
+               return leafRefPath;
+       },
+
+       resolveLeafRefPath (catalogs, leafRefPath, fieldKey, path, container) {
+               let pathCopy = _.clone(path);
+               // Strip any prefixes
+               let leafRefPathCopy = leafRefPath.replace(/[\w\d]*:/g, '');
+               // Strip any spaces
+               leafRefPathCopy = leafRefPathCopy.replace(/\s/g, '');
+
+               // resolve any current paths
+               leafRefPathCopy = this.resolveCurrentPredicate(leafRefPathCopy, container, pathCopy);
+
+               // Split on delimiter (/)
+               const pathArray = leafRefPathCopy.split('/');
+               let fieldKeyArray = fieldKey.split(':');
+               let results = [];
+
+               // Check if relative path or not
+               // TODO: Below works but
+               // 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] == '..') {
+                               fieldKeyArray.splice(-1, 1);
+                               if (!isNaN(Number(fieldKeyArray[fieldKeyArray.length - 1]))) {
+                                       // found a number, so an index. strip it
+                                       fieldKeyArray.splice(-1, 1);
+                               }
+                               i--;
+                       }
+
+                       // traversed all .. - now traverse down
+                       if (fieldKeyArray.length == 1) {
+                               for (let key in catalogs) {
+                                       for (let subKey in catalogs[key]) {
+                                               let found = _.find(catalogs[key][subKey], {id: fieldKeyArray[0]});
+                                               if (found) {
+                                                       results = this.getResults(found, pathArray.splice(-i, i));
+                                                       return results;
+                                               }
+                                       }
+                               }
+                       } else if (fieldKeyArray.length == 2) {
+                               for (let key in catalogs) {
+                                       for (let subKey in catalogs[key]) {
+                                               let found = _.find(catalogs[key][subKey], {id: fieldKeyArray[0]});
+                                               if (found) {
+                                                       for (let foundKey in found) {
+                                                               let topLevel = _.find(found[foundKey], {id: fieldKeyArray[1]});
+                                                               if (topLevel) {
+                                                                       results = this.getResults(topLevel, pathArray.splice(-i, i));
+                                                                       return results;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       } 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');
+                       }
+               } else {
+                       // absolute path
+                       if (pathArray[0] == "") {
+                               pathArray.splice(0, 1);
+                       }
+
+                       let catalogKey = pathArray[0];
+                       let topLevelObject = {};
+
+                       topLevelObject[catalogKey] = catalogs[catalogKey];
+
+                       results = this.getAbsoluteResults(topLevelObject, pathArray);
+
+                       return results;
+               }
+       }
 }
index 144332c..ea57627 100644 (file)
@@ -61,6 +61,14 @@ class CatalogDataStore {
                this.registerAsync(CatalogDataSource);
                this.bindActions(CatalogDataSourceActions);
                this.bindActions(CatalogItemsActions);
+               this.exportPublicMethods({
+            getCatalogs: this.getCatalogs,
+            getCatalogItemById: this.getCatalogItemById,
+            getCatalogItemByUid: this.getCatalogItemByUid,
+            getTransientCatalogs: this.getTransientCatalogs,
+            getTransientCatalogItemById: this.getTransientCatalogItemById,
+            getTransientCatalogItemByUid: this.getTransientCatalogItemByUid
+        });
        }
 
        resetSelectionState = () => {
@@ -72,6 +80,10 @@ class CatalogDataStore {
                return this.catalogs || (this.catalogs = []);
        }
 
+       getTransientCatalogs() {
+               return this.state.catalogs || (this.state.catalogs = []);
+       }
+
        getAllSelectedCatalogItems() {
                return this.getCatalogs().reduce((r, d) => {
                        d.descriptors.forEach(d => {
@@ -95,12 +107,24 @@ class CatalogDataStore {
                }, [])[0];
        }
 
+       getTransientCatalogItemById(id) {
+               return this.getTransientCatalogs().reduce((r, catalog) => {
+                       return r.concat(catalog.descriptors.filter(d => d.id === id));
+               }, [])[0];
+       }
+
        getCatalogItemByUid(uid) {
                return this.getCatalogs().reduce((r, catalog) => {
                        return r.concat(catalog.descriptors.filter(d => UID.from(d) === uid));
                }, [])[0];
        }
 
+       getTransientCatalogItemByUid(uid) {
+               return this.getTransientCatalogs().reduce((r, catalog) => {
+                       return r.concat(catalog.descriptors.filter(d => UID.from(d) === uid));
+               }, [])[0];
+       }
+
        removeCatalogItem(deleteItem = {}) {
                this.getCatalogs().map(catalog => {
                        catalog.descriptors = catalog.descriptors.filter(d => d.id !== deleteItem.id);