Support updated data types in service import
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / operations / impl / PropertyOperation.java
index 1f4fca0..b5215dc 100644 (file)
@@ -21,6 +21,20 @@ package org.openecomp.sdc.be.model.operations.impl;
 
 import static org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR;
 
+import com.fasterxml.jackson.core.ObjectCodec;
+import com.fasterxml.jackson.databind.DeserializationContext;
+import com.fasterxml.jackson.databind.JsonNode;
+import com.google.common.collect.Maps;
+import com.google.gson.JsonArray;
+import com.google.gson.JsonDeserializationContext;
+import com.google.gson.JsonDeserializer;
+import com.google.gson.JsonElement;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParseException;
+import com.google.gson.JsonParser;
+import com.google.gson.JsonSerializationContext;
+import com.google.gson.JsonSerializer;
+import fj.data.Either;
 import java.io.IOException;
 import java.lang.reflect.Type;
 import java.util.ArrayList;
@@ -62,11 +76,13 @@ import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
 import org.openecomp.sdc.be.datatypes.elements.PropertyRule;
 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.Component;
 import org.openecomp.sdc.be.model.ComponentInstanceProperty;
 import org.openecomp.sdc.be.model.DataTypeDefinition;
 import org.openecomp.sdc.be.model.IComplexDefaultValue;
 import org.openecomp.sdc.be.model.PropertyConstraint;
 import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.validation.ToscaFunctionValidator;
 import org.openecomp.sdc.be.model.operations.api.DerivedFromOperation;
 import org.openecomp.sdc.be.model.operations.api.IPropertyOperation;
 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
@@ -82,29 +98,15 @@ import org.openecomp.sdc.be.model.tosca.constraints.ValidValuesConstraint;
 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
 import org.openecomp.sdc.be.resources.data.ComponentInstanceData;
 import org.openecomp.sdc.be.resources.data.DataTypeData;
+import org.openecomp.sdc.be.resources.data.ModelData;
 import org.openecomp.sdc.be.resources.data.PropertyData;
 import org.openecomp.sdc.be.resources.data.PropertyValueData;
 import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
 import org.openecomp.sdc.be.resources.data.UniqueIdData;
 import org.openecomp.sdc.common.log.wrappers.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.stereotype.Component;
-import com.fasterxml.jackson.core.ObjectCodec;
-import com.fasterxml.jackson.databind.DeserializationContext;
-import com.fasterxml.jackson.databind.JsonNode;
-import com.google.common.collect.Maps;
-import com.google.gson.JsonArray;
-import com.google.gson.JsonDeserializationContext;
-import com.google.gson.JsonDeserializer;
-import com.google.gson.JsonElement;
-import com.google.gson.JsonObject;
-import com.google.gson.JsonParseException;
-import com.google.gson.JsonParser;
-import com.google.gson.JsonSerializationContext;
-import com.google.gson.JsonSerializer;
-import fj.data.Either;
 
-@Component("property-operation")
+@org.springframework.stereotype.Component("property-operation")
 public class PropertyOperation extends AbstractOperation implements IPropertyOperation {
 
     private static final String AFTER_RETRIEVING_DERIVED_FROM_NODE_OF_STATUS_IS = "After retrieving DERIVED_FROM node of {}. status is {}";
@@ -118,15 +120,28 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe
     private static final String THE_VALUE_OF_PROPERTY_FROM_TYPE_IS_INVALID = "The value {} of property from type {} is invalid";
     private static final String PROPERTY = "Property";
     private static final String UPDATE_DATA_TYPE = "UpdateDataType";
-    private static Logger log = Logger.getLogger(PropertyOperation.class.getName());
-    private DerivedFromOperation derivedFromOperation;
+    private static final Logger log = Logger.getLogger(PropertyOperation.class.getName());
+    private final DerivedFromOperation derivedFromOperation;
+    private ToscaFunctionValidator toscaFunctionValidator;
+    private DataTypeOperation dataTypeOperation;
 
     @Autowired
-    public PropertyOperation(HealingJanusGraphGenericDao janusGraphGenericDao, DerivedFromOperation derivedFromOperation) {
+    public PropertyOperation(final HealingJanusGraphGenericDao janusGraphGenericDao, final DerivedFromOperation derivedFromOperation) {
         this.janusGraphGenericDao = janusGraphGenericDao;
         this.derivedFromOperation = derivedFromOperation;
     }
 
+    @Autowired
+    public void setToscaFunctionValidator(final ToscaFunctionValidator toscaFunctionValidator) {
+        this.toscaFunctionValidator = toscaFunctionValidator;
+    }
+
+    //circular dependency DataTypeOperation->ModelOperation->ModelElementOperation->PropertyOperation
+    @Autowired
+    public void setDataTypeOperation(DataTypeOperation dataTypeOperation) {
+        this.dataTypeOperation = dataTypeOperation;
+    }
+
     public PropertyDefinition convertPropertyDataToPropertyDefinition(PropertyData propertyDataResult, String propertyName, String resourceId) {
         log.debug("The object returned after create property is {}", propertyDataResult);
         PropertyDefinition propertyDefResult = new PropertyDefinition(propertyDataResult.getPropertyDataDefinition());
@@ -827,6 +842,13 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe
         }
         return true;
     }
+    
+    public boolean isPropertyTypeValid(final IComplexDefaultValue property, final Map<String, DataTypeDefinition> dataTypes) {
+        if (property == null) {
+            return false;
+        }
+        return ToscaPropertyType.isValidType(property.getType()) != null || dataTypes.containsKey(property.getType());
+    }
 
     @Override
     public ImmutablePair<String, Boolean> isPropertyInnerTypeValid(IComplexDefaultValue property, Map<String, DataTypeDefinition> dataTypes) {
@@ -1506,28 +1528,19 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe
         return Either.left(true);
     }
 
-    public Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> getAllDataTypes() {
-        Map<String, DataTypeDefinition> dataTypes = new HashMap<>();
-        Either<Map<String, DataTypeDefinition>, JanusGraphOperationStatus> result = Either.left(dataTypes);
-        Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypes = janusGraphGenericDao
-            .getByCriteria(NodeTypeEnum.DataType, null, DataTypeData.class);
-        if (getAllDataTypes.isRight()) {
-            JanusGraphOperationStatus status = getAllDataTypes.right().value();
-            if (status != JanusGraphOperationStatus.NOT_FOUND) {
-                return Either.right(status);
-            } else {
-                return result;
-            }
-        }
-        List<DataTypeData> list = getAllDataTypes.left().value();
-        if (list != null) {
-            log.trace("Number of data types to load is {}", list.size());
-            List<String> collect = list.stream().map(p -> p.getDataTypeDataDefinition().getName()).collect(Collectors.toList());
-            log.trace("The data types to load are {}", collect);
-            for (DataTypeData dataTypeData : list) {
-                log.trace("Going to fetch data type {}. uid is {}", dataTypeData.getDataTypeDataDefinition().getName(), dataTypeData.getUniqueId());
+    public Either<Map<String, Map<String, DataTypeDefinition>>, JanusGraphOperationStatus> getAllDataTypes() {
+        final Map<String, Map<String, DataTypeDefinition>> dataTypes = new HashMap<>();
+        Either<Map<String, Map<String, DataTypeDefinition>>, JanusGraphOperationStatus> result = Either.left(dataTypes);
+        final Map<String, DataTypeDefinition> allDataTypesFound = new HashMap<>();
+        
+        final Map<String, List<String>> dataTypeUidstoModels = dataTypeOperation.getAllDataTypeUidsToModels();
+
+        if (dataTypeUidstoModels != null) {
+            log.trace("Number of data types to load is {}", dataTypeUidstoModels.size());
+            for (Map.Entry<String, List<String>> entry : dataTypeUidstoModels.entrySet()) {
+                log.trace("Going to fetch data type with uid {}", entry.getKey());
                 Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeByUid = this
-                    .getAndAddDataTypeByUid(dataTypeData.getUniqueId(), dataTypes);
+                    .getAndAddDataTypeByUid(entry.getKey(), allDataTypesFound);
                 if (dataTypeByUid.isRight()) {
                     JanusGraphOperationStatus status = dataTypeByUid.right().value();
                     if (status == JanusGraphOperationStatus.NOT_FOUND) {
@@ -1535,13 +1548,21 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe
                     }
                     return Either.right(status);
                 }
+                for (final String model: entry.getValue()) {
+                    if (!dataTypes.containsKey(model)) {
+                        dataTypes.put(model, new HashMap<String, DataTypeDefinition>());
+                    }
+                    DataTypeDefinition dataTypeDefinition = allDataTypesFound.get(entry.getKey());
+                    dataTypes.get(model).put(dataTypeDefinition.getName(), dataTypeDefinition);
+                }
             }
+            
         }
         if (log.isTraceEnabled()) {
             if (result.isRight()) {
                 log.trace("After fetching all data types {}", result);
             } else {
-                Map<String, DataTypeDefinition> map = result.left().value();
+                Map<String, Map<String, DataTypeDefinition>> map = result.left().value();
                 if (map != null) {
                     String types = map.keySet().stream().collect(Collectors.joining(",", "[", "]"));
                     log.trace("After fetching all data types {} ", types);
@@ -1577,7 +1598,7 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe
             log.error(FAILED_TO_FETCH_PROPERTIES_OF_DATA_TYPE, uniqueId);
             return Either.right(propertiesStatus);
         }
-        allDataTypes.put(dataTypeDefinition.getName(), dataTypeDefinition);
+        allDataTypes.put(dataTypeDefinition.getUniqueId(), dataTypeDefinition);
         String derivedFrom = dataTypeDefinition.getDerivedFromName();
         if (allDataTypes.containsKey(derivedFrom)) {
             DataTypeDefinition parentDataTypeDefinition = allDataTypes.get(derivedFrom);
@@ -1606,30 +1627,32 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe
             }
             DataTypeDefinition parentDataTypeDefinition = dataTypeByUid.left().value();
             dataTypeDefinition.setDerivedFrom(parentDataTypeDefinition);
+            final var model = getModel(uniqueId);
+            if (StringUtils.isNotEmpty(model)) {
+                dataTypeDefinition.setModel(model);
+            }
         }
         result = Either.left(dataTypeDefinition);
         return result;
     }
 
+    private String getModel(final String uniqueId) {
+        final Either<ImmutablePair<ModelData, GraphEdge>, JanusGraphOperationStatus> model = janusGraphGenericDao.getParentNode(
+            UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, GraphEdgeLabels.MODEL_ELEMENT,
+            NodeTypeEnum.Model, ModelData.class);
+        return model.isLeft() ? model.left().value().getLeft().getName() : StringUtils.EMPTY;
+    }
+
     public Either<String, JanusGraphOperationStatus> checkInnerType(PropertyDataDefinition propDataDef) {
         String propertyType = propDataDef.getType();
         ToscaPropertyType type = ToscaPropertyType.isValidType(propertyType);
         return getInnerType(type, propDataDef::getSchema);
     }
 
-    public Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypeNodes() {
-        final Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypes =
-            janusGraphGenericDao.getByCriteria(NodeTypeEnum.DataType, null, DataTypeData.class);
-        if (getAllDataTypes.isRight() && getAllDataTypes.right().value() == JanusGraphOperationStatus.NOT_FOUND) {
-            return Either.left(Collections.emptyList());
-        }
-        return getAllDataTypes;
-    }
-
     public Either<Object, Boolean> validateAndUpdatePropertyValue(String propertyType, String value, boolean isValidate, String innerType,
                                                                   Map<String, DataTypeDefinition> dataTypes) {
         log.trace("Going to validate property value and its type. type = {}, value = {}", propertyType, value);
-        ToscaPropertyType type = getType(propertyType);
+        final ToscaPropertyType type = getType(propertyType);
         if (isValidate) {
             if (type == null) {
                 DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
@@ -1663,6 +1686,21 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe
         return validateAndUpdatePropertyValue(propertyType, value, true, innerType, dataTypes);
     }
 
+    public Either<Object, Boolean> validateAndUpdatePropertyValue(final Component containerComponent, final PropertyDataDefinition property,
+                                                                  final Map<String, DataTypeDefinition> dataTypes) {
+        if (property.isToscaFunction()) {
+            toscaFunctionValidator.validate(property, containerComponent);
+            property.setValue(property.getToscaFunction().getValue());
+            return Either.left(property.getValue());
+        }
+        Either<String, JanusGraphOperationStatus> checkInnerType = checkInnerType(property);
+        if (checkInnerType.isRight()) {
+            return Either.right(false);
+        }
+        final String innerType = checkInnerType.left().value();
+        return validateAndUpdatePropertyValue(property.getType(), property.getValue(), true, innerType, dataTypes);
+    }
+
     public <T extends GraphNode> Either<List<PropertyDefinition>, StorageOperationStatus> getAllPropertiesRec(String uniqueId, NodeTypeEnum nodeType,
                                                                                                               Class<T> clazz) {
         return this.findPropertiesOfNode(nodeType, uniqueId).right().bind(this::handleNotFoundProperties).left()
@@ -1784,13 +1822,13 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe
             String oldDerivedFromName = oldDataTypeDefinition.getDerivedFromName();
             String dataTypeName = newDataTypeDefinition.getName();
             List<PropertyDefinition> propertiesToAdd = new ArrayList<>();
-            if (isPropertyOmitted(newProperties, oldProperties, dataTypeName) || isPropertyTypeChanged(dataTypeName, newProperties, oldProperties,
-                propertiesToAdd) || isDerivedFromNameChanged(dataTypeName, newDerivedFromName, oldDerivedFromName)) {
+            if (isPropertyTypeChanged(dataTypeName, newProperties, oldProperties, propertiesToAdd)
+                    || isDerivedFromNameChanged(dataTypeName, newDerivedFromName, oldDerivedFromName)) {
                 log.debug("The new data type {} is invalid.", dataTypeName);
                 result = Either.right(StorageOperationStatus.CANNOT_UPDATE_EXISTING_ENTITY);
                 return result;
             }
-            if (propertiesToAdd == null || propertiesToAdd.isEmpty()) {
+            if (CollectionUtils.isEmpty(propertiesToAdd)) {
                 log.debug("No new properties has been defined in the new data type {}", newDataTypeDefinition);
                 result = Either.right(StorageOperationStatus.OK);
                 return result;
@@ -1897,44 +1935,6 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe
         return entryType;
     }
 
-    private boolean isPropertyOmitted(List<PropertyDefinition> newProperties, List<PropertyDefinition> oldProperties, String dataTypeName) {
-        boolean isValid = validateChangeInCaseOfEmptyProperties(newProperties, oldProperties, dataTypeName);
-        if (!isValid) {
-            log.debug("At least one property is missing in the new data type {}", dataTypeName);
-            return false;
-        }
-        if (newProperties != null && oldProperties != null) {
-            List<String> newProps = newProperties.stream().map(PropertyDataDefinition::getName).collect(Collectors.toList());
-            List<String> oldProps = oldProperties.stream().map(PropertyDataDefinition::getName).collect(Collectors.toList());
-            if (!newProps.containsAll(oldProps)) {
-                StringJoiner joiner = new StringJoiner(",", "[", "]");
-                newProps.forEach(joiner::add);
-                log.debug("Properties {} in data type {} are missing, but they already defined in the existing data type", joiner.toString(),
-                    dataTypeName);
-                return true;
-            }
-        }
-        return false;
-    }
-
-    private boolean validateChangeInCaseOfEmptyProperties(List<PropertyDefinition> newProperties, List<PropertyDefinition> oldProperties,
-                                                          String dataTypeName) {
-        if (newProperties != null) {
-            if (newProperties.isEmpty()) {
-                newProperties = null;
-            }
-        }
-        if (oldProperties != null) {
-            if (oldProperties.isEmpty()) {
-                oldProperties = null;
-            }
-        }
-        if ((newProperties == null && oldProperties == null) || (newProperties != null && oldProperties != null)) {
-            return true;
-        }
-        return false;
-    }
-
     private boolean isDerivedFromNameChanged(String dataTypeName, String newDerivedFromName, String oldDerivedFromName) {
         if (newDerivedFromName != null) {
             boolean isEqual = newDerivedFromName.equals(oldDerivedFromName);
@@ -2259,4 +2259,5 @@ public class PropertyOperation extends AbstractOperation implements IPropertyOpe
             return null;
         }
     }
+
 }