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',
18 'config-parameter-map': 'nsd.config-parameter-map',
22 function getPathForType(type
) {
23 if (exportInnerTypesMap
[type
]) {
24 return exportInnerTypesMap
[type
];
29 const uiStateToSave
= ['containerPositionMap'];
32 // Data serialization will be done on a meta model basis. That is,
33 // given a schema and data, retrieve from the data only that which is
34 // defined by the schema.
37 // serialize data for a list of properties
38 function serializeAll(properties
, data
) {
40 return properties
.reduce((obj
, p
) => {
41 return Object
.assign(obj
, p
.serialize(data
));
47 function serialize_container(data
) {
48 data
= data
[this.name
];
53 obj
[this.name
] = serializeAll(this.properties
, data
);
57 function serialize_list(data
) {
58 data
= data
[this.name
];
60 if (!Array
.isArray(data
)) {
61 return serializeAll(this.properties
, data
);
62 } else if (data
.length
) {
63 let list
= data
.reduce((c
, d
) => {
64 let obj
= serializeAll(this.properties
, d
);
72 obj
[this.name
] = list
;
80 function serialize_leaf(data
) {
81 let value
= data
[this.name
];
82 if (value
=== null || typeof value
=== 'undefined' || value
=== '') {
86 if (this['data-type'] === 'empty') {
87 value
= ''; // empty string does get sent as value
89 obj
[this.name
] = value
;
93 function serialize_leaf_empty(data
) {
94 let value
= data
[this.name
];
103 function serialize_leaf_list(data
) {
104 data
= data
[this.name
];
106 data
= data
.reduce((result
, value
) => {
107 if (value
!== '' && value
!== null && value
!== undefined && typeof value
!== 'object') {
114 obj
[this.name
] = data
;
121 function serialize_choice(data
) {
122 let keys
= Object
.keys(data
);
124 const chosen
= this.properties
.find(
125 c
=> c
.type
=== 'case' && c
.properties
&& c
.properties
.some(p
=> keys
.indexOf(p
.name
) > -1 && data
[p
.name
]));
126 return chosen
&& serializeAll(chosen
.properties
, data
);
131 function serialize_case(data
) {
132 return Serializer
.container
.call(this, data
);
135 // special ui data handler for leaf of type string named 'meta'
136 function serialize_meta(data
) {
137 let uiState
= data
['uiState'];
138 let meta
= uiState
&& _pick(uiState
, uiStateToSave
);
139 // if there is no uiState to save perhaps this was not a ui state property
140 return _isEmpty(meta
) ? null : {meta
: JSON
.stringify(meta
)};
143 function serialize_unsupported(data
) {
144 console
.error('unsupported property', property
);
148 function getSerializer(property
) {
149 switch (property
.name
) {
152 return serialize_meta
.bind(property
);
154 switch (property
.type
) {
156 return serialize_list
.bind(property
);
158 return serialize_container
.bind(property
);
160 return serialize_choice
.bind(property
);
162 return serialize_case
.bind(property
);
164 return serialize_leaf_list
.bind(property
);
166 switch (property
['data-type']){
168 return serialize_leaf_empty
.bind(property
);
170 return serialize_leaf
.bind(property
);
172 return serialize_unsupported
.bind(property
);
175 let modelMetaByPropertyNameMap
= [];
177 let cachedDescriptorModelMetaRequest
= null;
181 if (!cachedDescriptorModelMetaRequest
) {
182 cachedDescriptorModelMetaRequest
= new Promise(function (resolve
, reject
) {
183 CommonUtils
.getDescriptorModelMeta().then(function (data
) {
184 let DescriptorModelMetaJSON
= data
;
185 modelMetaByPropertyNameMap
= Object
.keys(DescriptorModelMetaJSON
).reduce((map
, key
) => {
186 function mapProperties(parentMap
, parentObj
) {
187 // let's beef up the meta info with a helper (more to come?)
188 parentObj
.serialize
= getSerializer(parentObj
);
189 parentMap
[':meta'] = parentObj
;
190 const properties
= parentObj
&& parentObj
.properties
? parentObj
.properties
: [];
191 properties
.forEach(p
=> {
192 const colonIndex
= p
.name
.indexOf(':');
193 if (colonIndex
> 0) {
194 p
.name
= p
.name
.slice(colonIndex
+1);
196 parentMap
[p
.name
] = mapProperties({}, assign(p
, {
197 ':qualified-type': parentObj
[':qualified-type'] + '.' + p
.name
203 map
[key
] = mapProperties({}, assign(DescriptorModelMetaJSON
[key
], {
204 ':qualified-type': key
210 // initialize the UI centric properties that CONFD could care less about
211 _set(modelMetaByPropertyNameMap
, 'nsd.meta.:meta.preserve-line-breaks', true);
212 _set(modelMetaByPropertyNameMap
, 'vnfd.meta.:meta.preserve-line-breaks', true);
213 _set(modelMetaByPropertyNameMap
, 'vnfd.vdu.cloud-init.:meta.preserve-line-breaks', true);
214 _set(modelMetaByPropertyNameMap
, 'nsd.constituent-vnfd.vnf-configuration.config-template.:meta.preserve-line-breaks', true);
217 }, function (error
) {
218 cachedDescriptorModelMetaRequest
= null;
223 return cachedDescriptorModelMetaRequest
;
226 * Create a new instance of the indicated property and, if relevent, use the given
227 * unique name for the instance's key (see generateItemUniqueName)
229 * @param {Object | string} typeOrPath a property definition object or a path to a property
230 * @param [{string}] uniqueName optional
233 createModelInstanceForType(typeOrPath
, uniqueName
) {
234 const modelMeta
= this.getModelMetaForType(typeOrPath
);
235 return DescriptorModelMetaProperty
.createModelInstance(modelMeta
, uniqueName
);
237 getModelMetaForType(typeOrPath
, filterProperties
= () => true) {
238 // resolve paths like 'nsd' or 'vnfd.vdu' or 'nsd.constituent-vnfd'
239 const found
= _get(modelMetaByPropertyNameMap
, getPathForType(typeOrPath
));
241 const uiState
= _cloneDeep(found
[':meta']);
242 uiState
.properties
= uiState
.properties
.filter(filterProperties
);
245 console
.warn('no model uiState found for type', typeOrPath
);
247 getModelFieldNamesForType(typeOrPath
) {
248 // resolve paths like 'nsd' or 'vnfd.vdu' or 'nsd.constituent-vnfd'
249 const found
= _get(modelMetaByPropertyNameMap
, getPathForType(typeOrPath
));
252 found
[':meta'].properties
.map((p
) => {
254 if (p
.type
== 'choice') {
256 return p
.properties
.map(function (q
) {
257 result
.push(q
.properties
[0].name
);
261 return result
.push(p
.name
);
266 console
.warn('no model uiState found for type', typeOrPath
);
269 * For a list with a single valued key that is of type string, generate a unique name
270 * for a new entry to be added to the indicated list. This name will use the provided
271 * prefix (or the list's name) followed by a number. The number will be based on the
272 * current length of the array but will insure there is no collision with an existing
275 * @param {Array} list the list model data
276 * @param {prooerty} property the schema definition of the list
277 * @param [{any} prefix] the perferred prefix for the name. If not provide property.name
281 generateItemUniqueName(list
, property
, prefix
) {
282 if (property
.type
!== 'list' ||
283 property
.key
.length
!== 1 ||
284 property
.properties
.find(prop
=> prop
.name
=== property
.key
[0])['data-type'] !== 'string') {
285 // only support list with a single key of type string
289 prefix
= property
.name
;
291 let key
= property
.key
[0];
292 let suffix
= list
? list
.length
+ 1 : 1
293 let keyValue
= prefix
+ '-' + suffix
;
295 function makeUniqueName() {
297 for (let i
= 0; i
< list
.length
; i
= ++i
) {
298 if (list
[i
][key
] === keyValue
) {
299 keyValue
= keyValue
+ '-' + (i
+ 1);
300 makeUniqueName(); // not worried about recursing too deep (chances ??)