2 * Created by onvelocity on 1/27/16.
4 * This class provides methods to get the metadata about descriptor models.
7 import _cloneDeep
from 'lodash/cloneDeep'
8 import _isEmpty
from 'lodash/isEmpty'
9 import _pick
from 'lodash/pick'
10 import _get
from 'lodash/get'
11 import _set
from 'lodash/set'
12 import DescriptorModelMetaProperty
from './DescriptorModelMetaProperty'
13 import CommonUtils
from 'utils/utils';
14 const assign
= Object
.assign
;
16 const exportInnerTypesMap
= {
17 'constituent-vnfd': 'nsd.constituent-vnfd',
21 function getPathForType(type
) {
22 if (exportInnerTypesMap
[type
]) {
23 return exportInnerTypesMap
[type
];
28 const uiStateToSave
= ['containerPositionMap'];
31 // Data serialization will be done on a meta model basis. That is,
32 // given a schema and data, retrieve from the data only that which is
33 // defined by the schema.
36 // serialize data for a list of properties
37 function serializeAll(properties
, data
) {
39 return properties
.reduce((obj
, p
) => {
40 return Object
.assign(obj
, p
.serialize(data
));
46 function serialize_container(data
) {
47 data
= data
[this.name
];
52 obj
[this.name
] = serializeAll(this.properties
, data
);
56 function serialize_list(data
) {
57 data
= data
[this.name
];
59 if (!Array
.isArray(data
)) {
60 return serializeAll(this.properties
, data
);
61 } else if (data
.length
) {
62 let list
= data
.reduce((c
, d
) => {
63 let obj
= serializeAll(this.properties
, d
);
71 obj
[this.name
] = list
;
79 function serialize_leaf(data
) {
80 let value
= data
[this.name
];
81 if (value
=== null || typeof value
=== 'undefined' || value
=== '') {
85 if (this['data-type'] === 'empty') {
86 value
= ''; // empty string does get sent as value
88 obj
[this.name
] = value
;
92 function serialize_leaf_empty(data
) {
93 let value
= data
[this.name
];
102 function serialize_leaf_list(data
) {
103 data
= data
[this.name
];
105 commaSeparatedValues
= data
.reduce((d
, v
) => {
106 let leaf
= Serializer
.leaf
.call(this, d
);
107 let value
= leaf
& leaf
[this.name
];
108 if (value
&& value
.length
) {
115 if (commaSeparatedValues
.length
) {
117 obj
[this.name
] = commaSeparatedValues
;
124 function serialize_choice(data
) {
125 let keys
= Object
.keys(data
);
127 const chosen
= this.properties
.find(
128 c
=> c
.type
=== 'case' && c
.properties
&& c
.properties
.some(p
=> keys
.indexOf(p
.name
) > -1));
129 return chosen
&& serializeAll(chosen
.properties
, data
);
134 function serialize_case(data
) {
135 return Serializer
.container
.call(this, data
);
138 // special ui data handler for leaf of type string named 'meta'
139 function serialize_meta(data
) {
140 let uiState
= data
['uiState'];
141 let meta
= uiState
&& _pick(uiState
, uiStateToSave
);
142 // if there is no uiState to save perhaps this was not a ui state property
143 return _isEmpty(meta
) ? null : {meta
: JSON
.stringify(meta
)};
146 function serialize_unsupported(data
) {
147 console
.error('unsupported property', property
);
151 function getSerializer(property
) {
152 switch (property
.name
) {
155 return serialize_meta
.bind(property
);
157 switch (property
.type
) {
159 return serialize_list
.bind(property
);
161 return serialize_container
.bind(property
);
163 return serialize_choice
.bind(property
);
165 return serialize_case
.bind(property
);
167 return serialize_leaf_list
.bind(property
);
169 switch (property
['data-type']){
171 return serialize_leaf_empty
.bind(property
);
173 return serialize_leaf
.bind(property
);
175 return serialize_unsupported
.bind(property
);
178 let modelMetaByPropertyNameMap
= [];
180 let cachedDescriptorModelMetaRequest
= null;
184 if (!cachedDescriptorModelMetaRequest
) {
185 cachedDescriptorModelMetaRequest
= new Promise(function (resolve
, reject
) {
186 CommonUtils
.getDescriptorModelMeta().then(function (data
) {
187 let DescriptorModelMetaJSON
= data
;
188 modelMetaByPropertyNameMap
= Object
.keys(DescriptorModelMetaJSON
).reduce((map
, key
) => {
189 function mapProperties(parentMap
, parentObj
) {
190 // let's beef up the meta info with a helper (more to come?)
191 parentObj
.serialize
= getSerializer(parentObj
);
192 parentMap
[':meta'] = parentObj
;
193 const properties
= parentObj
&& parentObj
.properties
? parentObj
.properties
: [];
194 properties
.forEach(p
=> {
195 parentMap
[p
.name
] = mapProperties({}, assign(p
, {
196 ':qualified-type': parentObj
[':qualified-type'] + '.' + p
.name
202 map
[key
] = mapProperties({}, assign(DescriptorModelMetaJSON
[key
], {
203 ':qualified-type': key
209 // initialize the UI centric properties that CONFD could care less about
210 _set(modelMetaByPropertyNameMap
, 'nsd.meta.:meta.preserve-line-breaks', true);
211 _set(modelMetaByPropertyNameMap
, 'vnfd.meta.:meta.preserve-line-breaks', true);
212 _set(modelMetaByPropertyNameMap
, 'vnfd.vdu.cloud-init.:meta.preserve-line-breaks', true);
213 _set(modelMetaByPropertyNameMap
, 'nsd.constituent-vnfd.vnf-configuration.config-template.:meta.preserve-line-breaks', true);
216 }, function (error
) {
217 cachedDescriptorModelMetaRequest
= null;
222 return cachedDescriptorModelMetaRequest
;
225 * Create a new instance of the indicated property and, if relevent, use the given
226 * unique name for the instance's key (see generateItemUniqueName)
228 * @param {Object | string} typeOrPath a property definition object or a path to a property
229 * @param [{string}] uniqueName optional
232 createModelInstanceForType(typeOrPath
, uniqueName
) {
233 const modelMeta
= this.getModelMetaForType(typeOrPath
);
234 return DescriptorModelMetaProperty
.createModelInstance(modelMeta
, uniqueName
);
236 getModelMetaForType(typeOrPath
, filterProperties
= () => true) {
237 // resolve paths like 'nsd' or 'vnfd.vdu' or 'nsd.constituent-vnfd'
238 const found
= _get(modelMetaByPropertyNameMap
, getPathForType(typeOrPath
));
240 const uiState
= _cloneDeep(found
[':meta']);
241 uiState
.properties
= uiState
.properties
.filter(filterProperties
);
244 console
.warn('no model uiState found for type', typeOrPath
);
246 getModelFieldNamesForType(typeOrPath
) {
247 // resolve paths like 'nsd' or 'vnfd.vdu' or 'nsd.constituent-vnfd'
248 const found
= _get(modelMetaByPropertyNameMap
, getPathForType(typeOrPath
));
251 found
[':meta'].properties
.map((p
) => {
253 if (p
.type
== 'choice') {
255 return p
.properties
.map(function (q
) {
256 result
.push(q
.properties
[0].name
);
260 return result
.push(p
.name
);
265 console
.warn('no model uiState found for type', typeOrPath
);
268 * For a list with a single valued key that is of type string, generate a unique name
269 * for a new entry to be added to the indicated list. This name will use the provided
270 * prefix (or the list's name) followed by a number. The number will be based on the
271 * current length of the array but will insure there is no collision with an existing
274 * @param {Array} list the list model data
275 * @param {prooerty} property the schema definition of the list
276 * @param [{any} prefix] the perferred prefix for the name. If not provide property.name
280 generateItemUniqueName(list
, property
, prefix
) {
281 if (property
.type
!== 'list' ||
282 property
.key
.length
!== 1 ||
283 property
.properties
.find(prop
=> prop
.name
=== property
.key
[0])['data-type'] !== 'string') {
284 // only support list with a single key of type string
288 prefix
= property
.name
;
290 let key
= property
.key
[0];
291 let suffix
= list
? list
.length
+ 1 : 1
292 let keyValue
= prefix
+ '-' + suffix
;
294 function makeUniqueName() {
296 for (let i
= 0; i
< list
.length
; i
= ++i
) {
297 if (list
[i
][key
] === keyValue
) {
298 keyValue
= keyValue
+ '-' + (i
+ 1);
299 makeUniqueName(); // not worried about recursing too deep (chances ??)