aboutsummaryrefslogtreecommitdiffstats
path: root/catalog-be/src/main/java/org/openecomp/sdc/be/components/property/DefaultPropertyDecelerator.java
diff options
context:
space:
mode:
Diffstat (limited to 'catalog-be/src/main/java/org/openecomp/sdc/be/components/property/DefaultPropertyDecelerator.java')
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/property/DefaultPropertyDecelerator.java413
1 files changed, 413 insertions, 0 deletions
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/property/DefaultPropertyDecelerator.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/property/DefaultPropertyDecelerator.java
new file mode 100644
index 0000000000..e053096480
--- /dev/null
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/property/DefaultPropertyDecelerator.java
@@ -0,0 +1,413 @@
+package org.openecomp.sdc.be.components.property;
+
+import com.google.gson.Gson;
+import fj.data.Either;
+import org.apache.commons.collections.CollectionUtils;
+import org.apache.commons.collections.MapUtils;
+import org.json.simple.JSONObject;
+import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.GetInputValueDataDefinition;
+import org.openecomp.sdc.be.datatypes.elements.PropertiesOwner;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
+import org.openecomp.sdc.be.impl.ComponentsUtils;
+import org.openecomp.sdc.be.model.Component;
+import org.openecomp.sdc.be.model.ComponentInstancePropInput;
+import org.openecomp.sdc.be.model.IComponentInstanceConnectedElement;
+import org.openecomp.sdc.be.model.InputDefinition;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
+import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
+import org.openecomp.sdc.exception.ResponseFormat;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.yaml.snakeyaml.Yaml;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static org.openecomp.sdc.common.api.Constants.GET_INPUT;
+
+public abstract class DefaultPropertyDecelerator<PROPERTYOWNER extends PropertiesOwner, PROPERTYTYPE extends PropertyDataDefinition> implements PropertyDecelerator {
+
+ private static final Logger log = LoggerFactory.getLogger(DefaultPropertyDecelerator.class);
+ private static final short LOOP_PROTECTION_LEVEL = 10;
+ private final Gson gson = new Gson();
+ private ComponentsUtils componentsUtils;
+ private PropertyOperation propertyOperation;
+
+ public DefaultPropertyDecelerator(ComponentsUtils componentsUtils, PropertyOperation propertyOperation) {
+ this.componentsUtils = componentsUtils;
+ this.propertyOperation = propertyOperation;
+ }
+
+ @Override
+ public Either<List<InputDefinition>, StorageOperationStatus> declarePropertiesAsInputs(Component component, String propertiesOwnerId, List<ComponentInstancePropInput> propsToDeclare) {
+ log.debug("#declarePropertiesAsInputs - declaring properties as inputs for component {} from properties owner {}", component.getUniqueId(), propertiesOwnerId);
+ return resolvePropertiesOwner(component, propertiesOwnerId)
+ .map(propertyOwner -> declarePropertiesAsInputs(component, propertyOwner, propsToDeclare))
+ .orElse(Either.right(onPropertiesOwnerNotFound(component.getUniqueId(), propertiesOwnerId)));
+ }
+
+ abstract PROPERTYTYPE createDeclaredProperty(PropertyDataDefinition prop);
+
+ abstract Either<?, StorageOperationStatus> updatePropertiesValues(Component component, String propertiesOwnerId, List<PROPERTYTYPE> properties);
+
+ abstract Optional<PROPERTYOWNER> resolvePropertiesOwner(Component component, String propertiesOwnerId);
+
+ abstract void addPropertiesListToInput(PROPERTYTYPE declaredProp, PropertyDataDefinition originalProp, InputDefinition input);
+
+ private StorageOperationStatus onPropertiesOwnerNotFound(String componentId, String propertiesOwnerId) {
+ log.debug("#declarePropertiesAsInputs - properties owner {} was not found on component {}", propertiesOwnerId, componentId);
+ return StorageOperationStatus.NOT_FOUND;
+ }
+
+ private Either<List<InputDefinition>, StorageOperationStatus> declarePropertiesAsInputs(Component component, PropertiesOwner propertiesOwner, List<ComponentInstancePropInput> propsToDeclare) {
+ PropertiesDeclarationData inputsProperties = createInputsAndOverridePropertiesValues(component.getUniqueId(), propertiesOwner, propsToDeclare);
+ return updatePropertiesValues(component, propertiesOwner.getUniqueId(), inputsProperties.getPropertiesToUpdate())
+ .left()
+ .map(updatePropsRes -> inputsProperties.getInputsToCreate());
+ }
+
+ private PropertiesDeclarationData createInputsAndOverridePropertiesValues(String componentId, PropertiesOwner propertiesOwner, List<ComponentInstancePropInput> propsToDeclare) {
+ List<PROPERTYTYPE> declaredProperties = new ArrayList<>();
+ List<InputDefinition> createdInputs = propsToDeclare.stream()
+ .map(propInput -> declarePropertyInput(componentId, propertiesOwner, declaredProperties, propInput))
+ .collect(Collectors.toList());
+ return new PropertiesDeclarationData(createdInputs, declaredProperties);
+ }
+
+ private InputDefinition declarePropertyInput(String componentId, PropertiesOwner propertiesOwner, List<PROPERTYTYPE> declaredProperties, ComponentInstancePropInput propInput) {
+ PropertyDataDefinition prop = resolveProperty(declaredProperties, propInput);
+ propInput.setOwnerId(null);
+ propInput.setParentUniqueId(null);
+ InputDefinition inputDefinition = createInput(componentId, propertiesOwner, propInput, prop);
+ PROPERTYTYPE declaredProperty = createDeclaredProperty(prop);
+ if(!declaredProperties.contains(declaredProperty)){
+ declaredProperties.add(declaredProperty);
+ }
+ addPropertiesListToInput(declaredProperty, prop, inputDefinition);
+ return inputDefinition;
+ }
+
+ private InputDefinition createInput(String componentId, PropertiesOwner propertiesOwner, ComponentInstancePropInput propInput, PropertyDataDefinition prop) {
+ String generatedInputName = generateInputName(propertiesOwner.getNormalizedName(), propInput);
+ return createInputFromProperty(componentId, propertiesOwner, generatedInputName, propInput, prop);
+ }
+
+ private String generateInputName(String inputName, ComponentInstancePropInput propInput) {
+ String[] parsedPropNames = propInput.getParsedPropNames();
+ if(parsedPropNames != null){
+ for(String str: parsedPropNames){
+ inputName += "_" + str;
+ }
+ } else {
+ inputName += "_" + propInput.getName();
+ }
+ return inputName;
+ }
+
+ private PropertyDataDefinition resolveProperty(List<PROPERTYTYPE> propertiesToCreate, ComponentInstancePropInput propInput) {
+ Optional<PROPERTYTYPE> resolvedProperty = propertiesToCreate.stream()
+ .filter(p -> p.getName().equals(propInput.getName()))
+ .findFirst();
+ return resolvedProperty.isPresent() ? resolvedProperty.get() : propInput;
+ }
+
+ private InputDefinition createInputFromProperty(String componentId, PropertiesOwner propertiesOwner, String inputName, ComponentInstancePropInput propInput, PropertyDataDefinition prop) {
+ String propertiesName = propInput.getPropertiesName() ;
+ PropertyDefinition selectedProp = propInput.getInput();
+ String[] parsedPropNames = propInput.getParsedPropNames();
+ InputDefinition input;
+ boolean complexProperty = false;
+ if(propertiesName != null && !propertiesName.isEmpty() && selectedProp != null){
+ complexProperty = true;
+ input = new InputDefinition(selectedProp);
+ }else{
+ input = new InputDefinition(prop);
+ }
+ input.setDefaultValue(prop.getValue());
+ input.setName(inputName);
+ input.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(componentId, input.getName()));
+ input.setInputPath(propertiesName);
+ input.setInstanceUniqueId(propertiesOwner.getUniqueId());
+ input.setPropertyId(propInput.getUniqueId());
+ changePropertyValueToGetInputValue(inputName, parsedPropNames, input, prop, complexProperty);
+ ((IComponentInstanceConnectedElement)prop).setComponentInstanceId(propertiesOwner.getUniqueId());
+ ((IComponentInstanceConnectedElement)prop).setComponentInstanceName(propertiesOwner.getName());
+ return input;
+ }
+
+ private void changePropertyValueToGetInputValue(String inputName, String[] parsedPropNames, InputDefinition input, PropertyDataDefinition prop, boolean complexProperty) {
+ JSONObject jobject = new JSONObject();
+ if(prop.getValue() == null || prop.getValue().isEmpty()){
+ if(complexProperty){
+
+ jobject = createJSONValueForProperty(parsedPropNames.length -1, parsedPropNames, jobject, inputName);
+ prop.setValue(jobject.toJSONString());
+
+ }else{
+
+ jobject.put(GET_INPUT, input.getName());
+ prop.setValue(jobject.toJSONString());
+
+ }
+
+ }else{
+
+ String value = prop.getValue();
+ Object objValue = new Yaml().load(value);
+ if( objValue instanceof Map || objValue instanceof List){
+ if(!complexProperty){
+ jobject.put(GET_INPUT, input.getName());
+ prop.setValue(jobject.toJSONString());
+
+
+ }else{
+ Map<String, Object> mappedToscaTemplate = (Map<String, Object>) objValue;
+ createInputValue(mappedToscaTemplate, 1, parsedPropNames, inputName);
+
+ String json = gson.toJson(mappedToscaTemplate);
+ prop.setValue(json);
+
+ }
+
+ }else{
+ jobject.put(GET_INPUT, input.getName());
+ prop.setValue(jobject.toJSONString());
+
+ }
+
+ }
+
+
+ if(CollectionUtils.isEmpty(prop.getGetInputValues())){
+ prop.setGetInputValues(new ArrayList<>());
+ }
+ List<GetInputValueDataDefinition> getInputValues = prop.getGetInputValues();
+
+ GetInputValueDataDefinition getInputValueDataDefinition = new GetInputValueDataDefinition();
+ getInputValueDataDefinition.setInputId(input.getUniqueId());
+ getInputValueDataDefinition.setInputName(input.getName());
+ getInputValues.add(getInputValueDataDefinition);
+ }
+
+ private JSONObject createJSONValueForProperty (int i, String [] parsedPropNames, JSONObject ooj, String inputName){
+
+ while(i >= 1){
+ if( i == parsedPropNames.length -1){
+ JSONObject jobProp = new JSONObject();
+ jobProp.put(GET_INPUT, inputName);
+ ooj.put(parsedPropNames[i], jobProp);
+ i--;
+ return createJSONValueForProperty (i, parsedPropNames, ooj, inputName);
+ }else{
+ JSONObject res = new JSONObject();
+ res.put(parsedPropNames[i], ooj);
+ i --;
+ res = createJSONValueForProperty (i, parsedPropNames, res, inputName);
+ return res;
+ }
+ }
+
+ return ooj;
+ }
+
+ private Map<String, Object> createInputValue(Map<String, Object> lhm1, int index, String[] inputNames, String inputName){
+ while(index < inputNames.length){
+ if(lhm1.containsKey(inputNames[index])){
+ Object value = lhm1.get(inputNames[index]);
+ if (value instanceof Map){
+ if(index == inputNames.length -1){
+ ((Map) value).put(GET_INPUT, inputName);
+ return (Map) value;
+
+ }else{
+ index++;
+ return createInputValue((Map)value, index, inputNames, inputName);
+ }
+ }else{
+ Map<String, Object> jobProp = new HashMap<>();
+ if(index == inputNames.length -1){
+ jobProp.put(GET_INPUT, inputName);
+ lhm1.put(inputNames[index], jobProp);
+ return lhm1;
+ }else{
+ lhm1.put(inputNames[index], jobProp);
+ index++;
+ return createInputValue(jobProp, index, inputNames, inputName);
+ }
+ }
+ }else{
+ Map<String, Object> jobProp = new HashMap<>();
+ lhm1.put(inputNames[index], jobProp);
+ if(index == inputNames.length -1){
+ jobProp.put(GET_INPUT, inputName);
+ return jobProp;
+ }else{
+ index++;
+ return createInputValue(jobProp, index, inputNames, inputName);
+ }
+ }
+ }
+ return lhm1;
+ }
+
+ private class PropertiesDeclarationData {
+ private List<InputDefinition> inputsToCreate;
+ private List<PROPERTYTYPE> propertiesToUpdate;
+
+ PropertiesDeclarationData(List<InputDefinition> inputsToCreate, List<PROPERTYTYPE> propertiesToUpdate) {
+ this.inputsToCreate = inputsToCreate;
+ this.propertiesToUpdate = propertiesToUpdate;
+ }
+
+ List<InputDefinition> getInputsToCreate() {
+ return inputsToCreate;
+ }
+
+ List<PROPERTYTYPE> getPropertiesToUpdate() {
+ return propertiesToUpdate;
+ }
+ }
+
+ Either<InputDefinition, ResponseFormat> prepareValueBeforeDelete(InputDefinition inputForDelete, PropertyDataDefinition inputValue, List<String> pathOfComponentInstances) {
+ Either<InputDefinition, ResponseFormat> deleteEither = Either.left(inputForDelete);
+ String value = inputValue.getValue();
+ Map<String, Object> mappedToscaTemplate = (Map<String, Object>) new Yaml().load(value);
+
+ resetInputName(mappedToscaTemplate, inputForDelete.getName());
+
+ value = "";
+ if(!mappedToscaTemplate.isEmpty()){
+ Either result = cleanNestedMap(mappedToscaTemplate , true);
+ Map modifiedMappedToscaTemplate = mappedToscaTemplate;
+ if (result.isLeft())
+ modifiedMappedToscaTemplate = (Map)result.left().value();
+ else
+ log.warn("Map cleanup failed -> " +result.right().value().toString()); //continue, don't break operation
+ value = gson.toJson(modifiedMappedToscaTemplate);
+ }
+ inputValue.setValue(value);
+
+
+ List<GetInputValueDataDefinition> getInputsValues = inputValue.getGetInputValues();
+ if(getInputsValues != null && !getInputsValues.isEmpty()){
+ Optional<GetInputValueDataDefinition> op = getInputsValues.stream().filter(gi -> gi.getInputId().equals(inputForDelete.getUniqueId())).findAny();
+ if(op.isPresent()){
+ getInputsValues.remove(op.get());
+ }
+ }
+ inputValue.setGetInputValues(getInputsValues);
+
+ Either<String, TitanOperationStatus> findDefaultValue = propertyOperation.findDefaultValueFromSecondPosition(pathOfComponentInstances, inputValue.getUniqueId(), inputValue.getDefaultValue());
+ if (findDefaultValue.isRight()) {
+ deleteEither = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(DaoStatusConverter.convertTitanStatusToStorageStatus(findDefaultValue.right().value()))));
+ return deleteEither;
+
+ }
+ String defaultValue = findDefaultValue.left().value();
+ inputValue.setDefaultValue(defaultValue);
+ log.debug("The returned default value in ResourceInstanceProperty is {}", defaultValue);
+ return deleteEither;
+ }
+
+ private void resetInputName(Map<String, Object> lhm1, String inputName){
+ for (Map.Entry<String, Object> entry : lhm1.entrySet()) {
+ String key = entry.getKey();
+ Object value = entry.getValue();
+ if (value instanceof String && ((String) value).equalsIgnoreCase(inputName) && key.equals(GET_INPUT)) {
+ value = "";
+ lhm1.remove(key);
+ } else if (value instanceof Map) {
+ Map<String, Object> subMap = (Map<String, Object>)value;
+ resetInputName(subMap, inputName);
+ } else {
+ continue;
+ }
+
+ }
+ }
+
+ private Either cleanNestedMap( Map mappedToscaTemplate , boolean deepClone ){
+ if (MapUtils.isNotEmpty( mappedToscaTemplate ) ){
+ if (deepClone){
+ if (!(mappedToscaTemplate instanceof HashMap))
+ return Either.right("expecting mappedToscaTemplate as HashMap ,recieved "+ mappedToscaTemplate.getClass().getSimpleName() );
+ else
+ mappedToscaTemplate = (HashMap)((HashMap) mappedToscaTemplate).clone();
+ }
+ return Either.left( (Map) cleanEmptyNestedValuesInMap( mappedToscaTemplate , LOOP_PROTECTION_LEVEL ) );
+ }
+ else {
+ log.debug("mappedToscaTemplate is empty ");
+ return Either.right("mappedToscaTemplate is empty ");
+ }
+ }
+
+ /* Mutates the object
+ * Tail recurse -> traverse the tosca elements and remove nested empty map properties
+ * this only handles nested maps, other objects are left untouched (even a Set containing a map) since behaviour is unexpected
+ *
+ * @param toscaElement - expected map of tosca values
+ * @return mutated @param toscaElement , where empty maps are deleted , return null for empty map.
+ **/
+ private Object cleanEmptyNestedValuesInMap(Object toscaElement , short loopProtectionLevel ){
+ //region - Stop if map is empty
+ if (loopProtectionLevel<=0 || toscaElement==null || !(toscaElement instanceof Map))
+ return toscaElement;
+ //endregion
+ //region - Remove empty map entries & return null iff empty map
+ if ( MapUtils.isNotEmpty( (Map)toscaElement ) ) {
+ Object ret;
+ Set<Object> keysToRemove = new HashSet<>(); // use different set to avoid ConcurrentModificationException
+ for( Object key : ((Map)toscaElement).keySet() ) {
+ Object value = ((Map) toscaElement).get(key);
+ ret = cleanEmptyNestedValuesInMap(value , --loopProtectionLevel );
+ if ( ret == null )
+ keysToRemove.add(key);
+ }
+ Collection set = ((Map) toscaElement).keySet();
+ if (CollectionUtils.isNotEmpty(set))
+ set.removeAll(keysToRemove);
+
+ if ( isEmptyNestedMap(toscaElement) ) // similar to < if ( MapUtils.isEmpty( (Map)toscaElement ) ) > ,but adds nested map check
+ return null;
+ }
+ //endregion
+ else
+ return null;
+ return toscaElement;
+ }
+
+ //@returns true iff map nested maps are all empty
+ //ignores other collection objects
+ private boolean isEmptyNestedMap(Object element){
+ boolean isEmpty = true;
+ if (element != null){
+ if ( element instanceof Map ){
+ if (MapUtils.isEmpty((Map)element))
+ isEmpty = true;
+ else
+ {
+ for( Object key : ((Map)(element)).keySet() ){
+ Object value = ((Map)(element)).get(key);
+ isEmpty &= isEmptyNestedMap( value );
+ }
+ }
+ } else {
+ isEmpty = false;
+ }
+ }
+ return isEmpty;
+ }
+
+}