import '../styles/EditDescriptorModelProperties.scss'
+const EMPTY_LEAF_PRESENT = '--empty-leaf-set--';
+
function getDescriptorMetaBasicForType(type) {
const basicPropertiesFilter = d => _includes(DESCRIPTOR_MODEL_FIELDS[type], d.name);
return DescriptorModelMetaFactory.getModelMetaForType(type, basicPropertiesFilter) || {properties: []};
key={fieldKey}
id={fieldKey}
className={ClassNames({'-value-not-set': !isValueSet})}
- defaultValue={val && val.toUpperCase()} title={pathToProperty}
+ defaultValue={val && val.toUpperCase()}
+ title={pathToProperty}
+ onChange={onSelectChange}
+ onFocus={onFocus}
+ onBlur={endEditing}
+ onMouseDown={startEditing}
+ onMouseOver={startEditing}
+ readOnly={!isEditable}>
+ {options}
+ </select>
+ );
+ }
+
+ if (Property.isLeafEmpty(property)) {
+ // A null value indicates the leaf exists (as opposed to undefined).
+ // We stick in a string when the user actually sets it to simplify things
+ // but the correct thing happens when we serialize to user data
+ let isEmptyLeafPresent = (value === EMPTY_LEAF_PRESENT || value === null);
+ let present = isEmptyLeafPresent ? EMPTY_LEAF_PRESENT : "";
+ const options = [
+ <option key={'true'} value={EMPTY_LEAF_PRESENT}>Enabled</option>,
+ <option key={'false'} value="">Not Enabled</option>
+ ]
+
+ return (
+ <select
+ key={fieldKey}
+ id={fieldKey}
+ className={ClassNames({'-value-not-set': !isEmptyLeafPresent})}
+ defaultValue={present}
+ title={pathToProperty}
onChange={onSelectChange}
onFocus={onFocus}
onBlur={endEditing}
* This class provides methods to get the metadata about descriptor models.
*/
-'use strict';
-
import _cloneDeep from 'lodash/cloneDeep'
-import utils from './../utils'
+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;
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) {
+ commaSeparatedValues = data.reduce((d, v) => {
+ let leaf = Serializer.leaf.call(this, d);
+ let value = leaf & leaf[this.name];
+ if (value && value.length) {
+ if (v.length) {
+ v += ', ';
+ }
+ v += value;
+ }
+ }, "");
+ if (commaSeparatedValues.length) {
+ let obj = {};
+ obj[this.name] = commaSeparatedValues;
+ 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;
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;
})
})
},
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']);
uiState.properties = uiState.properties.filter(filterProperties);
},
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);
}
})
* 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') {
+ 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;
}
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);
+ keyValue = keyValue + '-' + (i + 1);
makeUniqueName(); // not worried about recursing too deep (chances ??)
break;
}
return keyValue;
}
-}
+}
\ No newline at end of file
* This class provides utility methods for interrogating an instance of model uiState object.
*/
-'use strict';
-
import _includes from 'lodash/includes'
import _isArray from 'lodash/isArray'
import guid from './../guid'
isBoolean(property = {}) {
return (typeof(property['data-type']) == 'string') && (property['data-type'].toLowerCase() == 'boolean')
},
+ isLeafEmpty(property = {}) {
+ return (typeof(property['data-type']) == 'string') && (property['data-type'].toLowerCase() == 'empty')
+ },
isLeaf(property = {}) {
return /leaf|choice/.test(property.type);
},
-
/*
*
* Copyright 2016 RIFT.IO Inc
* Created by onvelocity on 10/20/15.
*/
-import _isNumber from 'lodash/isNumber'
-import _cloneDeep from 'lodash/cloneDeep'
-import _isEmpty from 'lodash/isEmpty'
-import _omit from 'lodash/omit'
-import _pick from 'lodash/pick'
-import utils from './../utils'
-import DescriptorModelFields from './DescriptorModelFields'
import DescriptorModelMetaFactory from './DescriptorModelMetaFactory'
-let nsdFields = null;
-let vldFields = null;
-let vnfdFields = null;
-let cvnfdFields = null;
-
-
-
-
-/**
- * Serialize DescriptorModel JSON into CONFD JSON. Also, cleans up the data as needed.
- *
- * @type {{serialize: (function(*=)), ':clean': (function(*=)), nsd: {serialize: (function(*=))}, vld: {serialize: (function(*=))}, vnfd-connection-point-ref: {serialize: (function(*=))}, constituent-vnfd: {serialize: (function(*=))}, vnfd: {serialize: (function(*=))}, vdu: {serialize: (function(*=))}}}
- */
const DescriptorModelSerializer = {
+ /**
+ * Create a json object that can be sent to the backend. I.e. CONFD JSON compliant to the schema definition.
+ *
+ * @param {any} model - the data blob from the editor. This is not modified.
+ * @returns cleansed data model
+ */
serialize(model) {
- const type = model.uiState && model.uiState.type;
- const serializer = this[type];
- if (serializer) {
- model = serializer.serialize(model);
- this[':clean'](model);
- return model;
- }
- return false;
- },
- ':clean'(model) {
- // remove uiState from all elements accept nsd and vnfd
- // remove empty / blank value fields
- function clean(m) {
- Object.keys(m).forEach(k => {
- const isEmptyObject = typeof m[k] === 'object' && _isEmpty(m[k]);
- if (typeof m[k] === 'undefined' || isEmptyObject || m[k] === '') {
- delete m[k];
- }
- const isMetaAllowed = /^nsd|vnfd$/.test(m.uiState && m.uiState.type);
- if (k === 'uiState') {
- if (isMetaAllowed) {
- // remove any transient ui state properties
- const uiState = _pick(m.uiState, DescriptorModelFields.meta);
- if (!_isEmpty(uiState)) {
- // uiState field must be a string
- m['meta'] = JSON.stringify(uiState);
- }
- }
- delete m[k];
- }
- if (typeof m[k] === 'object') {
- clean(m[k]);
- }
- });
- }
- clean(model);
- return model;
- },
- nsd: {
- serialize(nsdModel) {
- if(!nsdFields) nsdFields = DescriptorModelMetaFactory.getModelFieldNamesForType('nsd').concat('uiState');
- const confd = _pick(nsdModel, nsdFields);
-
- // vnfd is defined in the ETSI etsi_gs reference manual but RIFT does not use it
- delete confd.vnfd;
-
- // map the vnfd instances into the CONFD constituent-vnfd ref instances
- confd['constituent-vnfd'] = confd['constituent-vnfd'].map((d, index) => {
-
- const constituentVNFD = {
- 'member-vnf-index': d['member-vnf-index'],
- 'vnfd-id-ref': d['vnfd-id-ref']
- };
-
- if (d['vnf-configuration']) {
- const vnfConfig = _cloneDeep(d['vnf-configuration']);
- const configType = vnfConfig['config-type'] || 'none';
- // make sure we send the correct values based on config type
- if (configType === 'none') {
- constituentVNFD['vnf-configuration'] = {'config-type': 'none'};
- const configPriority = utils.resolvePath(vnfConfig, 'input-params.config-priority');
- const configPriorityValue = _isNumber(configPriority) ? configPriority : d.uiState['member-vnf-index'];
- utils.assignPathValue(constituentVNFD['vnf-configuration'], 'input-params.config-priority', configPriorityValue);
- } else {
- // remove any unused configuration options
- ['netconf', 'rest', 'script', 'juju'].forEach(type => {
- if (configType !== type) {
- delete vnfConfig[type];
- }
- });
- constituentVNFD['vnf-configuration'] = vnfConfig;
- }
- }
-
- if (d.hasOwnProperty('start-by-default')) {
- constituentVNFD['start-by-default'] = d['start-by-default'];
- }
-
- return constituentVNFD;
-
- });
- for (var key in confd) {
- checkForChoiceAndRemove(key, confd, nsdModel);
- }
- // serialize the VLD instances
- confd.vld = confd.vld.map(d => {
- return DescriptorModelSerializer.serialize(d);
- });
-
- return cleanEmptyTopKeys(confd);
-
- }
- },
- vld: {
- serialize(vldModel) {
- if(!vldFields) vldFields = DescriptorModelMetaFactory.getModelFieldNamesForType('nsd.vld');
- const confd = _pick(vldModel, vldFields);
- const property = 'vnfd-connection-point-ref';
-
- // TODO: There is a bug in RIFT-REST that is not accepting empty
- // strings for string properties.
- // once that is fixed, remove this piece of code.
- // fix-start
- for (var key in confd) {
- if (confd.hasOwnProperty(key) && confd[key] === '') {
- delete confd[key];
- } else {
- //removes choice properties from top level object and copies immediate children onto it.
- checkForChoiceAndRemove(key, confd, vldModel);
- }
- }
-
-
- const deepProperty = 'provider-network';
- for (var key in confd[deepProperty]) {
- if (confd[deepProperty].hasOwnProperty(key) && confd[deepProperty][key] === '') {
- delete confd[deepProperty][key];
- }
- }
- // fix-end
- confd[property] = confd[property].map(d => DescriptorModelSerializer[property].serialize(d));
- return cleanEmptyTopKeys(confd);
- }
- },
- 'vnfd-connection-point-ref': {
- serialize(ref) {
- return _pick(ref, ['member-vnf-index-ref', 'vnfd-id-ref', 'vnfd-connection-point-ref']);
- }
- },
- 'internal-connection-point': {
- serialize(ref) {
- return _pick(ref, ['id-ref']);
- }
- },
- 'constituent-vnfd': {
- serialize(cvnfdModel) {
- if(!cvnfdFields) cvnfdFields = DescriptorModelMetaFactory.getModelFieldNamesForType('nsd.constituent-vnfd');
- return _pick(cvnfdModel, cvnfdFields);
- }
- },
- vnfd: {
- serialize(vnfdModel) {
- if(!vnfdFields) vnfdFields = DescriptorModelMetaFactory.getModelFieldNamesForType('vnfd').concat('uiState');
- const confd = _pick(vnfdModel, vnfdFields);
- confd.vdu = confd.vdu.map(d => DescriptorModelSerializer.serialize(d));
- return cleanEmptyTopKeys(confd);
- }
- },
- vdu: {
- serialize(vduModel) {
- const copy = _cloneDeep(vduModel);
- for (let k in copy) {
- checkForChoiceAndRemove(k, copy, vduModel)
- }
- const confd = _omit(copy, ['uiState']);
- return cleanEmptyTopKeys(confd);
- }
+ if (!model.uiState) {
+ console.error('model uiState null', model);
+ return {};
+ }
+ const path = model.uiState['qualified-type'] || model.uiState['type'];
+ const metaModel = DescriptorModelMetaFactory.getModelMetaForType(path);
+ const data = {};
+ const name = model.uiState['type'];
+ data[name] = model; // lets get the meta hierachy from the top
+ const result = metaModel.serialize(data);
+ console.debug(result);
+ return result;
}
-};
-
-
-function checkForChoiceAndRemove(k, confd, model) {
- let state = model.uiState;
- if (state.choice) {
- let choice = state.choice[k]
- if(choice) {
- if (choice.constructor.name == "Array") {
- for(let i = 0; i < choice.length; i++) {
- for (let key in confd[k][i]) {
- if(choice[i] && (choice[i].selected.indexOf(key) > -1)) {
- confd[k][i][key] = confd[k][i][key]
- }
- confd[key];
- };
- }
- } else {
- for (let key in confd[k]) {
- if(choice && (choice.selected.indexOf(key) > -1)) {
- confd[key] = confd[k][key]
- }
- };
- delete confd[k];
- }
-
- }
- }
- return confd;
-}
-
-function cleanEmptyTopKeys(m){
- Object.keys(m).forEach(k => {
- const isEmptyObject = typeof m[k] === 'object' && _isEmpty(m[k]);
- if (typeof m[k] === 'undefined' || isEmptyObject || m[k] === '') {
- delete m[k];
- }
- });
- return m;
}
-
-export default DescriptorModelSerializer;
+export default DescriptorModelSerializer;
\ No newline at end of file