RIFT-14828, RIFT-15031: choice/case for top level:2

Signed-off-by: Laurence Maultsby <laurence.maultsby@riftio.com>
diff --git a/skyquake/plugins/composer/src/src/components/EditDescriptorModelProperties.js b/skyquake/plugins/composer/src/src/components/EditDescriptorModelProperties.js
index 2c4b71c..20c84da 100644
--- a/skyquake/plugins/composer/src/src/components/EditDescriptorModelProperties.js
+++ b/skyquake/plugins/composer/src/src/components/EditDescriptorModelProperties.js
@@ -245,9 +245,10 @@
 
 				event.preventDefault();
 
-				const name = event.target.name;
+				let name = event.target.name;
 				const value = event.target.value;
 
+
 				/*
 					Transient State is stored for convenience in the uiState field.
 					The choice yang type uses case elements to describe the "options".
@@ -255,22 +256,22 @@
 					the system to determine which type is selected by the name of
 					the element contained within the field.
 				 */
-
-				//const stateExample = {
-				//	uiState: {
-				//		choice: {
-				//			'conf-config': {
-				//				selected: 'rest',
-				//				'case': {
-				//					rest: {},
-				//					netconf: {},
-				//					script: {}
-				//				}
-				//			}
-				//		}
-				//	}
-				//};
-
+				/*
+					const stateExample = {
+						uiState: {
+							choice: {
+								'conf-config': {
+									selected: 'rest',
+									'case': {
+										rest: {},
+										netconf: {},
+										script: {}
+									}
+								}
+							}
+						}
+					};
+				*/
 				const statePath = ['uiState.choice'].concat(name);
 				const stateObject = utils.resolvePath(this.model, statePath.join('.')) || {};
 				const selected = stateObject.selected ? stateObject.selected.split('.')[1] : undefined;
@@ -278,19 +279,34 @@
 				utils.assignPathValue(this.model, statePath.join('.'), stateObject);
 
 				// write the current choice value into the state
-				const choiceObject = utils.resolvePath(this.model, [name, selected].join('.'));
-				if (choiceObject) {
-					utils.assignPathValue(stateObject, ['case', selected].join('.'), _.cloneDeep(choiceObject));
+				let choiceObject = utils.resolvePath(this.model, [name, selected].join('.'));
+				let isTopCase = false;
+				if (!choiceObject) {
+					isTopCase = true;
+					choiceObject = utils.resolvePath(this.model, [selected].join('.'));
+				}
+				utils.assignPathValue(stateObject, [selected].join('.'), _.cloneDeep(choiceObject));
+
+				if(this.model.uiState.choice.hasOwnProperty(name)) {
+					delete this.model[selected];
+					utils.removePathValue(this.model, [name, selected].join('.'), isTopCase);
+				} else {
+					// remove the current choice value from the model
+				utils.removePathValue(this.model, [name, selected].join('.'), isTopCase);
 				}
 
-				// remove the current choice value from the model
-				utils.removePathValue(this.model, [name, selected].join('.'));
+
 
 				// get any state for the new selected choice
-				const newChoiceObject = utils.resolvePath(stateObject, ['case', value].join('.')) || {};
+				const newChoiceObject = utils.resolvePath(stateObject, [value].join('.')) || {};
 
 				// assign new choice value to the model
-				utils.assignPathValue(this.model, [name, value].join('.'), newChoiceObject);
+				if (isTopCase) {
+					utils.assignPathValue(this.model, [name, value].join('.'), newChoiceObject);
+				} else {
+					utils.assignPathValue(this.model, [value].join('.'), newChoiceObject)
+				}
+
 
 				// update the selected name
 				utils.assignPathValue(this.model, statePath.concat('selected').join('.'), value);
diff --git a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelFields.js b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelFields.js
index 40228ee..bc55760 100644
--- a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelFields.js
+++ b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelFields.js
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -27,7 +27,7 @@
 	nsd: common.concat(['constituent-vnfd', 'vnffgd', 'vld']),
 	vld: common.concat([]),
 	vnfd: common.concat(['vdu', 'internal-vld']),
-	'vnfd.vdu': common.concat(['image', 'external-interface', 'vm-flavor', 'cloud-init']),
+	'vnfd.vdu': common.concat(['image', 'external-interface', 'vm-flavor', 'cloud-init', 'filename']),
 	// white-list valid fields to send in the meta field
 	meta: ['containerPositionMap']
 };
diff --git a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaFactory.js b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaFactory.js
index 258b894..d3ef200 100644
--- a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaFactory.js
+++ b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelMetaFactory.js
@@ -82,7 +82,20 @@
 		// resolve paths like 'nsd' or 'vnfd.vdu' or 'nsd.constituent-vnfd'
 		const found = utils.resolvePath(modelMetaByPropertyNameMap, getPathForType(typeOrPath));
 		if (found) {
-			return found[':meta'].properties.map(p => p.name);
+			let result = [];
+			found[':meta'].properties.map((p) => {
+				// if(false) {
+				if(p.type == 'choice') {
+					result.push(p.name)
+					return p.properties.map(function(q){
+						result.push(q.properties[0].name);
+					})
+
+				} else  {
+					return result.push(p.name);
+				}
+			})
+			return result;
 		}
 		console.warn('no model uiState found for type', typeOrPath);
 	}
diff --git a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelSerializer.js b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelSerializer.js
index cebb946..014beb3 100644
--- a/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelSerializer.js
+++ b/skyquake/plugins/composer/src/src/libraries/model/DescriptorModelSerializer.js
@@ -121,7 +121,9 @@
 				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);
@@ -142,11 +144,12 @@
 			// 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];
-				}
-				//removes choice properties from top level object and copies immediate children onto it.
-				checkForChoiceAndRemove(key, confd, vldModel);
+			  	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);
+                }
 			}
 
 
@@ -157,8 +160,6 @@
 				}
 			}
 			// fix-end
-
-
 			confd[property] = confd[property].map(d => DescriptorModelSerializer[property].serialize(d));
 			return confd;
 		}
@@ -189,7 +190,7 @@
 	},
 	vdu: {
 		serialize(vduModel) {
-			const copy = _.clone(vduModel);
+			const copy = _.cloneDeep(vduModel);
 			for (let k in copy) {
 				checkForChoiceAndRemove(k, copy, vduModel)
 			}
@@ -201,17 +202,19 @@
 
 
 function checkForChoiceAndRemove(k, confd, model) {
-				let state = model.uiState;
-				if (state.choice) {
-					let choice = state.choice[k]
-					if(choice) {
-						for (let key in confd[k]) {
-							confd[key] = confd[k][key]
-						};
-						delete confd[k];
-					}
+	let state = model.uiState;
+	if (state.choice) {
+		let choice = state.choice[k]
+		if(choice) {
+			for (let key in confd[k]) {
+				if(choice && (choice.selected.indexOf(key) > -1)) {
+					confd[key] = confd[k][key]
 				}
-				return confd;
-			}
+			};
+			delete confd[k];
+		}
+	}
+	return confd;
+}
 
 export default DescriptorModelSerializer;
diff --git a/skyquake/plugins/composer/src/src/libraries/utils.js b/skyquake/plugins/composer/src/src/libraries/utils.js
index 01e6675..ec69bc4 100644
--- a/skyquake/plugins/composer/src/src/libraries/utils.js
+++ b/skyquake/plugins/composer/src/src/libraries/utils.js
@@ -1,5 +1,5 @@
 /*
- * 
+ *
  *   Copyright 2016 RIFT.IO Inc
  *
  *   Licensed under the Apache License, Version 2.0 (the "License");
@@ -71,7 +71,7 @@
 		}, obj);
 		resolvedObj[name] = value;
 	},
-	updatePathValue(obj, path, value) {
+	updatePathValue(obj, path, value, isCase) {
 		// todo: replace implementation of assignPathValue with this impl and
 		// remove updatePathValue (only need one function, not both)
 		// same as assignPathValue except removes property if value is undefined
@@ -92,18 +92,27 @@
 				if (isArray) {
 					r[p] = r[p].filter((d, i) => i !== index);
 				} else {
-					delete r[p][name];
+					if(isCase) {
+						delete r[name];
+					} else {
+						delete r[p][name];
+					}
 				}
 			}
-			return r[p];
+			if(isCase) {
+				return r;
+			} else {
+				return r[p];
+			}
+
 		}, obj);
 		if (!isRemove) {
 			resolvedObj[name] = value;
 		}
 	},
-	removePathValue(obj, path) {
+	removePathValue(obj, path, isCase) {
 		// note updatePathValue removes value if third argument is undefined
-		return this.updatePathValue(obj, path);
+		return this.updatePathValue(obj, path, undefined, isCase);
 	},
 
 	suffixAsInteger: (field) => {