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 data
= data
.reduce((result
, value
) => {
106 if (value
!== '' && value
!== null && value
!== undefined && typeof value
!== 'object') {
113 obj
[this.name
] = data
;
120 function serialize_choice(data
) {
121 let keys
= Object
.keys(data
);
123 const chosen
= this.properties
.find(
124 c
=> c
.type
=== 'case' && c
.properties
&& c
.properties
.some(p
=> keys
.indexOf(p
.name
) > -1));
125 return chosen
&& serializeAll(chosen
.properties
, data
);
130 function serialize_case(data
) {
131 return Serializer
.container
.call(this, data
);
134 // special ui data handler for leaf of type string named 'meta'
135 function serialize_meta(data
) {
136 let uiState
= data
['uiState'];
137 let meta
= uiState
&& _pick(uiState
, uiStateToSave
);
138 // if there is no uiState to save perhaps this was not a ui state property
139 return _isEmpty(meta
) ? null : {meta
: JSON
.stringify(meta
)};
142 function serialize_unsupported(data
) {
143 console
.error('unsupported property', property
);
147 function getSerializer(property
) {
148 switch (property
.name
) {
151 return serialize_meta
.bind(property
);
153 switch (property
.type
) {
155 return serialize_list
.bind(property
);
157 return serialize_container
.bind(property
);
159 return serialize_choice
.bind(property
);
161 return serialize_case
.bind(property
);
163 return serialize_leaf_list
.bind(property
);
165 switch (property
['data-type']){
167 return serialize_leaf_empty
.bind(property
);
169 return serialize_leaf
.bind(property
);
171 return serialize_unsupported
.bind(property
);
174 let modelMetaByPropertyNameMap
= [];
176 let cachedDescriptorModelMetaRequest
= null;
180 if (!cachedDescriptorModelMetaRequest
) {
181 cachedDescriptorModelMetaRequest
= new Promise(function (resolve
, reject
) {
182 CommonUtils
.getDescriptorModelMeta().then(function (data
) {
183 let DescriptorModelMetaJSON
= data
;
184 modelMetaByPropertyNameMap
= Object
.keys(DescriptorModelMetaJSON
).reduce((map
, key
) => {
185 function mapProperties(parentMap
, parentObj
) {
186 // let's beef up the meta info with a helper (more to come?)
187 parentObj
.serialize
= getSerializer(parentObj
);
188 parentMap
[':meta'] = parentObj
;
189 const properties
= parentObj
&& parentObj
.properties
? parentObj
.properties
: [];
190 properties
.forEach(p
=> {
191 parentMap
[p
.name
] = mapProperties({}, assign(p
, {
192 ':qualified-type': parentObj
[':qualified-type'] + '.' + p
.name
198 map
[key
] = mapProperties({}, assign(DescriptorModelMetaJSON
[key
], {
199 ':qualified-type': key
205 // initialize the UI centric properties that CONFD could care less about
206 _set(modelMetaByPropertyNameMap
, 'nsd.meta.:meta.preserve-line-breaks', true);
207 _set(modelMetaByPropertyNameMap
, 'vnfd.meta.:meta.preserve-line-breaks', true);
208 _set(modelMetaByPropertyNameMap
, 'vnfd.vdu.cloud-init.:meta.preserve-line-breaks', true);
209 _set(modelMetaByPropertyNameMap
, 'nsd.constituent-vnfd.vnf-configuration.config-template.:meta.preserve-line-breaks', true);
212 }, function (error
) {
213 cachedDescriptorModelMetaRequest
= null;
218 return cachedDescriptorModelMetaRequest
;
221 * Create a new instance of the indicated property and, if relevent, use the given
222 * unique name for the instance's key (see generateItemUniqueName)
224 * @param {Object | string} typeOrPath a property definition object or a path to a property
225 * @param [{string}] uniqueName optional
228 createModelInstanceForType(typeOrPath
, uniqueName
) {
229 const modelMeta
= this.getModelMetaForType(typeOrPath
);
230 return DescriptorModelMetaProperty
.createModelInstance(modelMeta
, uniqueName
);
232 getModelMetaForType(typeOrPath
, filterProperties
= () => true) {
233 // resolve paths like 'nsd' or 'vnfd.vdu' or 'nsd.constituent-vnfd'
234 const found
= _get(modelMetaByPropertyNameMap
, getPathForType(typeOrPath
));
236 const uiState
= _cloneDeep(found
[':meta']);
237 uiState
.properties
= uiState
.properties
.filter(filterProperties
);
240 console
.warn('no model uiState found for type', typeOrPath
);
242 getModelFieldNamesForType(typeOrPath
) {
243 // resolve paths like 'nsd' or 'vnfd.vdu' or 'nsd.constituent-vnfd'
244 const found
= _get(modelMetaByPropertyNameMap
, getPathForType(typeOrPath
));
247 found
[':meta'].properties
.map((p
) => {
249 if (p
.type
== 'choice') {
251 return p
.properties
.map(function (q
) {
252 result
.push(q
.properties
[0].name
);
256 return result
.push(p
.name
);
261 console
.warn('no model uiState found for type', typeOrPath
);
264 * For a list with a single valued key that is of type string, generate a unique name
265 * for a new entry to be added to the indicated list. This name will use the provided
266 * prefix (or the list's name) followed by a number. The number will be based on the
267 * current length of the array but will insure there is no collision with an existing
270 * @param {Array} list the list model data
271 * @param {prooerty} property the schema definition of the list
272 * @param [{any} prefix] the perferred prefix for the name. If not provide property.name
276 generateItemUniqueName(list
, property
, prefix
) {
277 if (property
.type
!== 'list' ||
278 property
.key
.length
!== 1 ||
279 property
.properties
.find(prop
=> prop
.name
=== property
.key
[0])['data-type'] !== 'string') {
280 // only support list with a single key of type string
284 prefix
= property
.name
;
286 let key
= property
.key
[0];
287 let suffix
= list
? list
.length
+ 1 : 1
288 let keyValue
= prefix
+ '-' + suffix
;
290 function makeUniqueName() {
292 for (let i
= 0; i
< list
.length
; i
= ++i
) {
293 if (list
[i
][key
] === keyValue
) {
294 keyValue
= keyValue
+ '-' + (i
+ 1);
295 makeUniqueName(); // not worried about recursing too deep (chances ??)