X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FUI.git;a=blobdiff_plain;f=skyquake%2Fplugins%2Fcomposer%2Fsrc%2Fsrc%2Flibraries%2Fmodel%2FDescriptorModelMetaFactory.js;h=1ba891256e6db5bb4b7cef4adaa941b8de88788d;hp=d3ef200fb041000c0d2b1d1353755a674224a3c4;hb=35808a0905d96e45bbe68c9b19cda2b673e1f9a6;hpb=25ceeb3e3d86705229fad71f9b6738b97ead7e36 diff --git a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaFactory.js b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaFactory.js index d3ef200fb..1ba891256 100644 --- a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaFactory.js +++ b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaFactory.js @@ -4,10 +4,11 @@ * This class provides methods to get the metadata about descriptor models. */ -'use strict'; - -import _ from 'lodash' -import utils from './../utils' +import _cloneDeep from 'lodash/cloneDeep' +import _isEmpty from 'lodash/isEmpty' +import _pick from 'lodash/pick' +import _get from 'lodash/get' +import _set from 'lodash/set' import DescriptorModelMetaProperty from './DescriptorModelMetaProperty' import CommonUtils from 'utils/utils'; const assign = Object.assign; @@ -24,6 +25,152 @@ function getPathForType(type) { return type; } +const uiStateToSave = ['containerPositionMap']; + +////// +// Data serialization will be done on a meta model basis. That is, +// given a schema and data, retrieve from the data only that which is +// defined by the schema. +// + +// serialize data for a list of properties +function serializeAll(properties, data) { + if (data) { + return properties.reduce((obj, p) => { + return Object.assign(obj, p.serialize(data)); + }, {}); + } + return null; +} + +function serialize_container(data) { + data = data[this.name]; + if (_isEmpty(data)) { + return null; + } + let obj = {}; + obj[this.name] = serializeAll(this.properties, data); + return obj; +} + +function serialize_list(data) { + data = data[this.name]; + if (data) { + if (!Array.isArray(data)) { + return serializeAll(this.properties, data); + } else if (data.length) { + let list = data.reduce((c, d) => { + let obj = serializeAll(this.properties, d); + if (!_isEmpty(obj)) { + c.push(obj); + } + return c; + }, []); + if (!_isEmpty(list)){ + let obj = {}; + obj[this.name] = list; + return obj; + } + } + } + return null; +} + +function serialize_leaf(data) { + let value = data[this.name]; + if (value === null || typeof value === 'undefined' || value === '') { + return null; + } + let obj = {}; + if (this['data-type'] === 'empty') { + value = ''; // empty string does get sent as value + } + obj[this.name] = value; + return obj; +} + +function serialize_leaf_empty(data) { + let value = data[this.name]; + if (value) { + let obj = {}; + obj[this.name] = ""; + return obj; + } + return null; +} + +function serialize_leaf_list(data) { + data = data[this.name]; + if (data) { + data = data.reduce((result, value) => { + if (value !== '' && value !== null && value !== undefined && typeof value !== 'object') { + result.push(value); + } + return result; + }, []); + if (data.length){ + let obj = {}; + obj[this.name] = data; + return obj; + } + } + return null; +} + +function serialize_choice(data) { + let keys = Object.keys(data); + if (keys) { + const chosen = this.properties.find( + c => c.type === 'case' && c.properties && c.properties.some(p => keys.indexOf(p.name) > -1)); + return chosen && serializeAll(chosen.properties, data); + } + return null; +} + +function serialize_case(data) { + return Serializer.container.call(this, data); +} + +// special ui data handler for leaf of type string named 'meta' +function serialize_meta(data) { + let uiState = data['uiState']; + let meta = uiState && _pick(uiState, uiStateToSave); + // if there is no uiState to save perhaps this was not a ui state property + return _isEmpty(meta) ? null : {meta: JSON.stringify(meta)}; +} + +function serialize_unsupported(data) { + console.error('unsupported property', property); + return null; +} + +function getSerializer(property) { + switch (property.name) { + case 'rw-nsd:meta': + case 'rw-vnfd:meta': + return serialize_meta.bind(property); + } + switch (property.type) { + case 'list': + return serialize_list.bind(property); + case 'container': + return serialize_container.bind(property); + case 'choice': + return serialize_choice.bind(property); + case 'case': + return serialize_case.bind(property); + case 'leaf_list': + return serialize_leaf_list.bind(property); + case 'leaf': + switch (property['data-type']){ + case 'empty': + return serialize_leaf_empty.bind(property); + } + return serialize_leaf.bind(property); + } + return serialize_unsupported.bind(property); +} + let modelMetaByPropertyNameMap = []; let cachedDescriptorModelMetaRequest = null; @@ -31,32 +178,38 @@ let cachedDescriptorModelMetaRequest = null; export default { init() { if (!cachedDescriptorModelMetaRequest) { - cachedDescriptorModelMetaRequest = new Promise(function(resolve, reject) { - CommonUtils.getDescriptorModelMeta().then(function(data) { + cachedDescriptorModelMetaRequest = new Promise(function (resolve, reject) { + CommonUtils.getDescriptorModelMeta().then(function (data) { let DescriptorModelMetaJSON = data; modelMetaByPropertyNameMap = Object.keys(DescriptorModelMetaJSON).reduce((map, key) => { function mapProperties(parentMap, parentObj) { + // let's beef up the meta info with a helper (more to come?) + parentObj.serialize = getSerializer(parentObj); parentMap[':meta'] = parentObj; const properties = parentObj && parentObj.properties ? parentObj.properties : []; properties.forEach(p => { - parentMap[p.name] = mapProperties({}, assign(p, {':qualified-type': parentObj[':qualified-type'] + '.' + p.name})); + parentMap[p.name] = mapProperties({}, assign(p, { + ':qualified-type': parentObj[':qualified-type'] + '.' + p.name + })); return map; }, parentMap); return parentMap; } - map[key] = mapProperties({}, assign(DescriptorModelMetaJSON[key], {':qualified-type': key})); + map[key] = mapProperties({}, assign(DescriptorModelMetaJSON[key], { + ':qualified-type': key + })); return map; }, {}); (() => { // initialize the UI centric properties that CONFD could care less about - utils.assignPathValue(modelMetaByPropertyNameMap, 'nsd.meta.:meta.preserve-line-breaks', true); - utils.assignPathValue(modelMetaByPropertyNameMap, 'vnfd.meta.:meta.preserve-line-breaks', true); - utils.assignPathValue(modelMetaByPropertyNameMap, 'vnfd.vdu.cloud-init.:meta.preserve-line-breaks', true); - utils.assignPathValue(modelMetaByPropertyNameMap, 'nsd.constituent-vnfd.vnf-configuration.config-template.:meta.preserve-line-breaks', true); + _set(modelMetaByPropertyNameMap, 'nsd.meta.:meta.preserve-line-breaks', true); + _set(modelMetaByPropertyNameMap, 'vnfd.meta.:meta.preserve-line-breaks', true); + _set(modelMetaByPropertyNameMap, 'vnfd.vdu.cloud-init.:meta.preserve-line-breaks', true); + _set(modelMetaByPropertyNameMap, 'nsd.constituent-vnfd.vnf-configuration.config-template.:meta.preserve-line-breaks', true); })(); resolve(); - }, function(error) { + }, function (error) { cachedDescriptorModelMetaRequest = null; }) }) @@ -64,15 +217,23 @@ export default { return cachedDescriptorModelMetaRequest; }, - createModelInstanceForType(typeOrPath) { + /** + * Create a new instance of the indicated property and, if relevent, use the given + * unique name for the instance's key (see generateItemUniqueName) + * + * @param {Object | string} typeOrPath a property definition object or a path to a property + * @param [{string}] uniqueName optional + * @returns + */ + createModelInstanceForType(typeOrPath, uniqueName) { const modelMeta = this.getModelMetaForType(typeOrPath); - return DescriptorModelMetaProperty.createModelInstance(modelMeta); + return DescriptorModelMetaProperty.createModelInstance(modelMeta, uniqueName); }, getModelMetaForType(typeOrPath, filterProperties = () => true) { // resolve paths like 'nsd' or 'vnfd.vdu' or 'nsd.constituent-vnfd' - const found = utils.resolvePath(modelMetaByPropertyNameMap, getPathForType(typeOrPath)); + const found = _get(modelMetaByPropertyNameMap, getPathForType(typeOrPath)); if (found) { - const uiState = _.cloneDeep(found[':meta']); + const uiState = _cloneDeep(found[':meta']); uiState.properties = uiState.properties.filter(filterProperties); return uiState; } @@ -80,23 +241,65 @@ export default { }, getModelFieldNamesForType(typeOrPath) { // resolve paths like 'nsd' or 'vnfd.vdu' or 'nsd.constituent-vnfd' - const found = utils.resolvePath(modelMetaByPropertyNameMap, getPathForType(typeOrPath)); + const found = _get(modelMetaByPropertyNameMap, getPathForType(typeOrPath)); if (found) { let result = []; found[':meta'].properties.map((p) => { // if(false) { - if(p.type == 'choice') { + if (p.type == 'choice') { result.push(p.name) - return p.properties.map(function(q){ + return p.properties.map(function (q) { result.push(q.properties[0].name); }) - } else { + } else { return result.push(p.name); } }) return result; } console.warn('no model uiState found for type', typeOrPath); + }, + /** + * For a list with a single valued key that is of type string, generate a unique name + * for a new entry to be added to the indicated list. This name will use the provided + * prefix (or the list's name) followed by a number. The number will be based on the + * current length of the array but will insure there is no collision with an existing + * name. + * + * @param {Array} list the list model data + * @param {prooerty} property the schema definition of the list + * @param [{any} prefix] the perferred prefix for the name. If not provide property.name + * will be used. + * @returns {string} + */ + generateItemUniqueName(list, property, prefix) { + if (property.type !== 'list' || + property.key.length !== 1 || + property.properties.find(prop => prop.name === property.key[0])['data-type'] !== 'string') { + // only support list with a single key of type string + return null; + } + if (!prefix) { + prefix = property.name; + } + let key = property.key[0]; + let suffix = list ? list.length + 1 : 1 + let keyValue = prefix + '-' + suffix; + + function makeUniqueName() { + if (list) { + for (let i = 0; i < list.length; i = ++i) { + if (list[i][key] === keyValue) { + keyValue = keyValue + '-' + (i + 1); + makeUniqueName(); // not worried about recursing too deep (chances ??) + break; + } + } + } + } + makeUniqueName(); + return keyValue; } -} + +} \ No newline at end of file