VFC Property default value enforced forced to comply with restraints
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / datamodel / utils / PropertyValueConstraintValidationUtil.java
index 25ca7d6..74cf8eb 100644 (file)
@@ -33,14 +33,20 @@ import org.apache.commons.lang3.ArrayUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
 import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
+import org.openecomp.sdc.be.datatypes.elements.SubPropertyToscaFunction;
+import org.openecomp.sdc.be.model.ComponentInstanceInput;
 import org.openecomp.sdc.be.model.DataTypeDefinition;
 import org.openecomp.sdc.be.model.InputDefinition;
 import org.openecomp.sdc.be.model.PropertyConstraint;
 import org.openecomp.sdc.be.model.PropertyDefinition;
 import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
+import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
 import org.openecomp.sdc.be.model.tosca.ToscaType;
 import org.openecomp.sdc.be.model.tosca.constraints.ConstraintUtil;
-import org.openecomp.sdc.be.model.tosca.constraints.ValidValuesConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.LengthConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.MaxLengthConstraint;
+import org.openecomp.sdc.be.model.tosca.constraints.MinLengthConstraint;
 import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
 import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
 import org.openecomp.sdc.exception.ResponseFormat;
@@ -52,7 +58,12 @@ public class PropertyValueConstraintValidationUtil {
     private static final String UNDERSCORE = "_";
     private static final String VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY = "%nValue provided in invalid format for %s property";
     private static final Logger logger = LoggerFactory.getLogger(PropertyValueConstraintValidationUtil.class);
-    private static final String IGNORE_PROPERTY_VALUE_START_WITH = "{\"get_input\":";
+    private static final String IGNORE_PROPERTY_VALUE_START_WITH_INPUT = "{\"get_input\":";
+    private static final String IGNORE_PROPERTY_VALUE_START_WITH_PROPERTY = "{\"get_property\":";
+    private static final String IGNORE_PROPERTY_VALUE_START_WITH_ATTRIBUTE = "{\"get_attribute\":";
+    private static final String IGNORE_PROPERTY_VALUE_INPUT = "{get_input=";
+    private static final String IGNORE_PROPERTY_VALUE_PROPERTY = "{get_property=";
+    private static final String IGNORE_PROPERTY_VALUE_ATTRIBUTE = "{get_attribute=";
     private Map<String, DataTypeDefinition> dataTypeDefinitionCache;
     private final ObjectMapper objectMapper = new ObjectMapper();
     private final List<String> errorMessages = new ArrayList<>();
@@ -65,7 +76,7 @@ public class PropertyValueConstraintValidationUtil {
 
         dataTypeDefinitionCache = applicationDataTypeCache.getAll(model).left().value();
         CollectionUtils.emptyIfNull(propertyDefinitionList).stream()
-            .filter(this::isValuePresent)
+            .filter(this::isNonToscaFunctionValuePresent)
             .forEach(this::evaluatePropertyTypeForConstraintValidation);
         if (CollectionUtils.isNotEmpty(errorMessages)) {
             final String errorMsgAsString = String.join(",", errorMessages);
@@ -75,32 +86,46 @@ public class PropertyValueConstraintValidationUtil {
         return Either.left(Boolean.TRUE);
     }
 
-    private boolean isValuePresent(PropertyDefinition propertyDefinition) {
+    private boolean isNonToscaFunctionValuePresent(PropertyDefinition propertyDefinition) {
+        if (isValueAToscaFunction(propertyDefinition)) {
+            return false;
+        }
+        if (propertyDefinition instanceof ComponentInstanceInput) {
+            return StringUtils.isNotEmpty(propertyDefinition.getValue());
+        }
         if (propertyDefinition instanceof InputDefinition) {
             return StringUtils.isNotEmpty(propertyDefinition.getDefaultValue());
         }
-        return StringUtils.isNotEmpty(propertyDefinition.getValue());
+        return StringUtils.isNotEmpty(propertyDefinition.getValue() != null ? propertyDefinition.getValue() : propertyDefinition.getDefaultValue());
     }
 
     private void evaluatePropertyTypeForConstraintValidation(PropertyDefinition propertyDefinition) {
-        if (propertyDefinition == null || propertyDefinition.getType() == null || !dataTypeDefinitionCache.containsKey(propertyDefinition.getType())) {
+        if (propertyDefinition == null || propertyDefinition.getType() == null || !dataTypeDefinitionCache.containsKey(
+            propertyDefinition.getType())) {
             errorMessages.add("\nUnsupported datatype found for property " + getCompletePropertyName(propertyDefinition));
             return;
         }
         completeInputName = "";
         completePropertyName = new StringBuilder();
+        if (propertyDefinition instanceof ComponentInstanceInput) {
+            setCompletePropertyName(propertyDefinition);
+            evaluateComplexTypeProperties(propertyDefinition);
+            return;
+        }
         if (propertyDefinition instanceof InputDefinition) {
             completeInputName = propertyDefinition.getName();
             propertyDefinition = getPropertyDefinitionObjectFromInputs(propertyDefinition);
         }
         if (propertyDefinition != null) {
+            List<PropertyConstraint> propertyConstraints =
+                dataTypeDefinitionCache.get(propertyDefinition.getType()).safeGetConstraints();
             if (ToscaType.isPrimitiveType(propertyDefinition.getType())) {
                 propertyDefinition.setConstraints(org.openecomp.sdc.be.dao.utils.CollectionUtils.merge(propertyDefinition.safeGetConstraints(),
-                    dataTypeDefinitionCache.get(propertyDefinition.getType()).safeGetConstraints()));
+                    propertyConstraints.isEmpty() ? new ArrayList<>() : propertyConstraints));
                 evaluateConstraintsOnProperty(propertyDefinition);
             } else if (ToscaType.isCollectionType(propertyDefinition.getType())) {
                 propertyDefinition.setConstraints(org.openecomp.sdc.be.dao.utils.CollectionUtils.merge(propertyDefinition.safeGetConstraints(),
-                    dataTypeDefinitionCache.get(propertyDefinition.getType()).safeGetConstraints()));
+                    propertyConstraints.isEmpty() ? new ArrayList<>() : propertyConstraints));
                 evaluateConstraintsOnProperty(propertyDefinition);
                 evaluateCollectionTypeProperties(propertyDefinition);
             } else {
@@ -118,26 +143,30 @@ public class PropertyValueConstraintValidationUtil {
 
     private void evaluateConstraintsOnProperty(PropertyDefinition propertyDefinition) {
         ToscaType toscaType = ToscaType.isValidType(propertyDefinition.getType());
-        if (isPropertyNotMappedAsInput(propertyDefinition) && CollectionUtils.isNotEmpty(propertyDefinition.getConstraints())
-            && isValidValueConstraintPresent(propertyDefinition.getConstraints())) {
+        if (!isValueAToscaFunction(propertyDefinition) && CollectionUtils.isNotEmpty(propertyDefinition.getConstraints())) {
             for (PropertyConstraint propertyConstraint : propertyDefinition.getConstraints()) {
                 try {
-                    propertyConstraint.initialize(toscaType);
-                    propertyConstraint.validate(toscaType, propertyDefinition.getValue());
+                    propertyConstraint.initialize(toscaType, propertyDefinition.getSchema());
+                    propertyConstraint.validate(propertyDefinition);
                 } catch (ConstraintValueDoNotMatchPropertyTypeException | ConstraintViolationException exception) {
-                    errorMessages.add("\n" + propertyConstraint.getErrorMessage(toscaType, exception, getCompletePropertyName(propertyDefinition)));
+                    errorMessages.add(propertyConstraint.getErrorMessage(toscaType, exception, getCompletePropertyName(propertyDefinition)));
+                } catch (IllegalArgumentException ie) {
+                    errorMessages.add(ie.getMessage());
                 }
             }
-        } else if (isPropertyNotMappedAsInput(propertyDefinition) && ToscaType.isPrimitiveType(propertyDefinition.getType()) && !toscaType
-            .isValidValue(propertyDefinition.getValue())) {
-            errorMessages.add(String
-                .format("\nUnsupported value provided for %s property supported value " + "type is %s.", getCompletePropertyName(propertyDefinition),
-                    toscaType.getType()));
+        } else if (!isValueAToscaFunction(propertyDefinition) && ToscaType.isPrimitiveType(propertyDefinition.getType())
+                && !propertyDefinition.isToscaFunction() && !toscaType.isValidValue(
+                    propertyDefinition.getValue() != null ? propertyDefinition.getValue() : propertyDefinition.getDefaultValue())) {
+            errorMessages.add(String.format("Unsupported value provided for %s property supported value type is %s.",
+                getCompletePropertyName(propertyDefinition), toscaType.getType()));
         }
     }
 
-    private boolean isPropertyNotMappedAsInput(PropertyDefinition propertyDefinition) {
-        return !propertyDefinition.getValue().startsWith(IGNORE_PROPERTY_VALUE_START_WITH);
+    private boolean isValueAToscaFunction(PropertyDefinition propertyDefinition) {
+        return (propertyDefinition.getToscaFunction() != null)  || (propertyDefinition.getValue() != null
+            && ((propertyDefinition.getValue().startsWith(IGNORE_PROPERTY_VALUE_START_WITH_INPUT) || propertyDefinition.getValue().startsWith(IGNORE_PROPERTY_VALUE_START_WITH_PROPERTY)
+            || propertyDefinition.getValue().startsWith(IGNORE_PROPERTY_VALUE_START_WITH_ATTRIBUTE) || propertyDefinition.getValue().startsWith(IGNORE_PROPERTY_VALUE_ATTRIBUTE)
+            || propertyDefinition.getValue().startsWith(IGNORE_PROPERTY_VALUE_PROPERTY) || propertyDefinition.getValue().startsWith(IGNORE_PROPERTY_VALUE_INPUT))));
     }
 
     private void checkAndEvaluatePrimitiveProperty(PropertyDefinition propertyDefinition, DataTypeDefinition dataTypeDefinition) {
@@ -169,25 +198,79 @@ public class PropertyValueConstraintValidationUtil {
 
     private void evaluateRegularComplexType(PropertyDefinition propertyDefinition, PropertyDefinition prop, Map<String, Object> valueMap) {
         try {
+            PropertyDefinition newPropertyWithValue;
             if (valueMap.containsKey(prop.getName())) {
+                if (propertyDefinition.getSubPropertyToscaFunctions() != null) {
+                    for (SubPropertyToscaFunction subPropertyToscaFunction : propertyDefinition.getSubPropertyToscaFunctions()) {
+                        final List<String> path = subPropertyToscaFunction.getSubPropertyPath();
+                        if (path.size() == 1 && path.get(0).equals(prop.getName())) {
+                            return;
+                        }
+                        if (path.size() > 1) {
+                            if (path.get(0).equals(propertyDefinition.getToscaSubPath()) && path.get(1).equals(prop.getName())) {
+                                return;
+                            }
+                        }
+                    }
+                }
                 if (ToscaType.isPrimitiveType(prop.getType())) {
-                    evaluateConstraintsOnProperty(copyPropertyWithNewValue(prop, String.valueOf(valueMap.get(prop.getName()))));
+                    newPropertyWithValue = copyPropertyWithNewValue(prop, String.valueOf(valueMap.get(prop.getName())), prop.getName());
+                    if (isPropertyToEvaluate(newPropertyWithValue)) {
+                        evaluateConstraintsOnProperty(newPropertyWithValue);
+                    }
                 } else if (ToscaType.isCollectionType(prop.getType())) {
-                    evaluateCollectionTypeProperties(copyPropertyWithNewValue(prop, objectMapper.writeValueAsString(valueMap.get(prop.getName()))));
+                    newPropertyWithValue =
+                        copyPropertyWithNewValue(prop,
+                            objectMapper.writeValueAsString(valueMap.get(prop.getName())), prop.getName());
+                    if (isPropertyToEvaluate(newPropertyWithValue)) {
+                        evaluateCollectionTypeProperties(newPropertyWithValue);
+                    }
                 } else {
-                    completePropertyName.append(UNDERSCORE);
-                    completePropertyName.append(prop.getName());
-                    evaluateComplexTypeProperties(copyPropertyWithNewValue(prop, objectMapper.writeValueAsString(valueMap.get(prop.getName()))));
+                    newPropertyWithValue =
+                        copyPropertyWithNewValue(prop,
+                            objectMapper.writeValueAsString(valueMap.get(prop.getName())), prop.getName());
+                    if (isPropertyToEvaluate(newPropertyWithValue)) {
+                        evaluateComplexTypeProperties(newPropertyWithValue);
+                    }
                 }
             }
-        } catch (IOException e) {
+        } catch (IOException | ConstraintValueDoNotMatchPropertyTypeException e) {
             logger.error(e.getMessage(), e);
             errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY, getCompletePropertyName(propertyDefinition)));
         }
     }
 
+    private boolean isPropertyToEvaluate(PropertyDefinition propertyDefinition) throws ConstraintValueDoNotMatchPropertyTypeException {
+        if (Boolean.FALSE.equals(propertyDefinition.isRequired())) {
+            if (!ToscaType.isCollectionType(propertyDefinition.getType())) {
+                return StringUtils.isNotEmpty(propertyDefinition.getValue()) &&
+                    !"null".equals(propertyDefinition.getValue());
+            } else if (ToscaType.LIST == ToscaType.isValidType(propertyDefinition.getType())) {
+                Collection<?> list = ConstraintUtil.parseToCollection(null != propertyDefinition.getValue() ?
+                    propertyDefinition.getValue() : propertyDefinition.getDefaultValue(), new TypeReference<List<?>>() {
+                });
+                return CollectionUtils.isNotEmpty(list);
+            } else {
+                Map<String, Object> valueMap = MapUtils
+                    .emptyIfNull(ConstraintUtil.parseToCollection(propertyDefinition.getValue(), new TypeReference<>() {
+                    }));
+                return MapUtils.isNotEmpty(valueMap);
+            }
+        } else {
+            return true;
+        }
+    }
+
     private void evaluateCollectionTypeProperties(PropertyDefinition propertyDefinition) {
         ToscaType toscaPropertyType = ToscaType.isValidType(propertyDefinition.getType());
+        try {
+            if (isPropertyToEvaluate(propertyDefinition)) {
+                evaluateCollectionConstraints(propertyDefinition, toscaPropertyType);
+            }
+        } catch (ConstraintValueDoNotMatchPropertyTypeException e) {
+            logger.error(e.getMessage(), e);
+            errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY, getCompletePropertyName(propertyDefinition)));
+        }
         if (ToscaType.LIST == toscaPropertyType) {
             evaluateListType(propertyDefinition);
         } else if (ToscaType.MAP == toscaPropertyType) {
@@ -195,22 +278,84 @@ public class PropertyValueConstraintValidationUtil {
         }
     }
 
+    private void evaluateCollectionConstraints(PropertyDefinition propertyDefinition, ToscaType toscaPropertyType) {
+        List<PropertyConstraint> constraintsList = propertyDefinition.getConstraints();
+
+        if (CollectionUtils.isEmpty(constraintsList)) {
+            return;
+        }
+        ToscaType toscaPropertyType1;
+        if (null == toscaPropertyType) {
+            toscaPropertyType1 = ToscaType.isValidType(propertyDefinition.getType());
+        } else {
+            toscaPropertyType1 = toscaPropertyType;
+        }
+        constraintsList.stream()
+            .filter(this::isACollectionConstraint)
+            .forEach(propertyConstraint -> {
+                try {
+                    if (ToscaType.LIST == toscaPropertyType1) {
+                        Collection<Object> list = ConstraintUtil.parseToCollection(propertyDefinition.getValue(), new TypeReference<>() {
+                        });
+                        propertyConstraint.validate(list);
+                    } else if (ToscaType.MAP == toscaPropertyType1) {
+                        final Map<String, Object> map = ConstraintUtil.parseToCollection(propertyDefinition.getValue(), new TypeReference<>() {
+                        });
+                        propertyConstraint.validate(map);
+                    }
+                } catch (ConstraintValueDoNotMatchPropertyTypeException | ConstraintViolationException exception) {
+                    errorMessages.add("\n" + propertyConstraint.getErrorMessage(toscaPropertyType1, exception,
+                        getCompletePropertyName(propertyDefinition)));
+                }
+            });
+    }
+
+    private boolean isACollectionConstraint(PropertyConstraint constraint) {
+        if (constraint instanceof MaxLengthConstraint) {
+            return true;
+        }
+        if (constraint instanceof MinLengthConstraint) {
+            return true;
+        }
+        return constraint instanceof LengthConstraint;
+    }
+
     private void evaluateListType(PropertyDefinition propertyDefinition) {
         try {
-            String schemaType = propertyDefinition.getSchemaType();
-            List<Object> list = ConstraintUtil.parseToCollection(propertyDefinition.getValue(), new TypeReference<>() {});
-            evaluateCollectionType(propertyDefinition, list, schemaType);
+            if (propertyDefinition.getSchemaType() == null) {
+                propertyDefinition.setSchema(createStringSchema());
+            }
+            Collection<?> list = ConstraintUtil.parseToCollection(null != propertyDefinition.getValue() ?
+                propertyDefinition.getValue() : propertyDefinition.getDefaultValue(), new TypeReference<List<?>>() {});
+            final Map<String, Object> map = new HashMap<>();
+            int index = 0;
+            for (Object obj : list) {
+                map.put(String.valueOf(index),obj);
+                index++;
+            }
+            evaluateCollectionType(propertyDefinition, map);
         } catch (ConstraintValueDoNotMatchPropertyTypeException e) {
             logger.debug(e.getMessage(), e);
             errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY, getCompletePropertyName(propertyDefinition)));
         }
     }
 
-    private void evaluateMapType(PropertyDefinition propertyDefinition) {
+    private SchemaDefinition createStringSchema() {
+        final SchemaDefinition schemaDefinition = new SchemaDefinition();
+        final PropertyDefinition schemaStringProperty = new PropertyDefinition();
+        schemaStringProperty.setType(ToscaType.STRING.getType());
+        schemaDefinition.setProperty(schemaStringProperty);
+        return schemaDefinition;
+    }
+
+    private void evaluateMapType(final PropertyDefinition propertyDefinition) {
         try {
-            String schemaType = propertyDefinition.getSchemaType();
-            Map<String, Object> map = ConstraintUtil.parseToCollection(propertyDefinition.getValue(), new TypeReference<>() {});
-            evaluateCollectionType(propertyDefinition, map.values(), schemaType);
+            if (propertyDefinition.getSchemaType() == null) {
+                propertyDefinition.setSchema(createStringSchema());
+            }
+            final Map<String, Object> map = ConstraintUtil.parseToCollection(propertyDefinition.getValue(), new TypeReference<>() {
+            });
+            evaluateCollectionType(propertyDefinition, map);
         } catch (ConstraintValueDoNotMatchPropertyTypeException e) {
             logger.debug(e.getMessage(), e);
             errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY, getCompletePropertyName(propertyDefinition)));
@@ -227,21 +372,27 @@ public class PropertyValueConstraintValidationUtil {
         }
     }
 
-    private void evaluateCollectionType(final PropertyDefinition propertyDefinition, final Collection<Object> valueList, final String schemaType) {
-        for (final Object value : valueList) {
+    private void evaluateCollectionType(final PropertyDefinition propertyDefinition, final Map<String, Object> valueMap) {
+        final String schemaType = propertyDefinition.getSchemaType();
+        for (String mapKey : valueMap.keySet()) {
+            final Object value = valueMap.get(mapKey);
             try {
-                final PropertyDefinition propertyDefinition1 = copyPropertyWithNewValue(propertyDefinition, objectMapper.writeValueAsString(value));
-                if (ToscaType.isPrimitiveType(schemaType)) {
-                    evaluateCollectionPrimitiveSchemaType(propertyDefinition1, schemaType);
-                } else if (ToscaType.isCollectionType(schemaType)) {
-                    propertyDefinition1.setType(schemaType);
-                    propertyDefinition1.setSchemaType(propertyDefinition.getSchemaProperty().getSchemaType());
-                    evaluateCollectionTypeProperties(propertyDefinition1);
-                } else {
-                    propertyDefinition1.setType(schemaType);
-                    completePropertyName.append(UNDERSCORE);
-                    completePropertyName.append(propertyDefinition1.getName());
-                    evaluateComplexTypeProperties(propertyDefinition1);
+                final PropertyDefinition propertyCopyWithNewValue = copyPropertyWithNewValue(propertyDefinition,
+                    objectMapper.writeValueAsString(value),mapKey);
+                propertyCopyWithNewValue.setToscaSubPath(mapKey);
+                if (!isValueAToscaFunction(propertyCopyWithNewValue)) {
+                    if (ToscaType.isPrimitiveType(schemaType)) {
+                        evaluateCollectionPrimitiveSchemaType(propertyCopyWithNewValue, schemaType);
+                    } else if (ToscaType.isCollectionType(schemaType)) {
+                        propertyCopyWithNewValue.setType(schemaType);
+                        propertyCopyWithNewValue.setSchemaType(propertyDefinition.getSchemaProperty().getSchemaType());
+                        evaluateCollectionTypeProperties(propertyCopyWithNewValue);
+                    } else {
+                        propertyCopyWithNewValue.setType(schemaType);
+                        completePropertyName.append(UNDERSCORE);
+                        completePropertyName.append(propertyCopyWithNewValue.getName());
+                        evaluateComplexTypeProperties(propertyCopyWithNewValue);
+                    }
                 }
             } catch (final Exception e) {
                 logger.debug(e.getMessage(), e);
@@ -263,16 +414,20 @@ public class PropertyValueConstraintValidationUtil {
         return propertyName;
     }
 
-    private PropertyDefinition copyPropertyWithNewValue(final PropertyDefinition propertyToCopy, final String value) {
+    private PropertyDefinition copyPropertyWithNewValue(final PropertyDefinition propertyToCopy, final String value, final String key) {
         final var propertyDefinition = new PropertyDefinition(propertyToCopy);
+        if (key != null && propertyToCopy.getSubPropertyToscaFunctions() != null) {
+            propertyToCopy.getSubPropertyToscaFunctions().forEach(subPropertyToscaFunction -> {
+                final List<String> subPropertyPath = subPropertyToscaFunction.getSubPropertyPath();
+                if (subPropertyPath.get((subPropertyPath.size() - 1)).equals(key)) {
+                    propertyDefinition.setToscaFunction(subPropertyToscaFunction.getToscaFunction());
+                }
+            });
+        }
         propertyDefinition.setValue(value);
         return propertyDefinition;
     }
 
-    private boolean isValidValueConstraintPresent(List<PropertyConstraint> propertyConstraints) {
-        return propertyConstraints != null && propertyConstraints.stream().anyMatch(ValidValuesConstraint.class::isInstance);
-    }
-
     private PropertyDefinition getPropertyDefinitionObjectFromInputs(PropertyDefinition property) {
         InputDefinition inputDefinition = (InputDefinition) property;
         PropertyDefinition propertyDefinition = null;
@@ -281,8 +436,10 @@ public class PropertyValueConstraintValidationUtil {
             propertyDefinition.setType(inputDefinition.getType());
             propertyDefinition.setValue(inputDefinition.getDefaultValue());
             propertyDefinition.setName(inputDefinition.getName());
+            propertyDefinition.setConstraints(inputDefinition.getConstraints());
         } else if (Objects.nonNull(inputDefinition.getInputPath())) {
             propertyDefinition = evaluateComplexTypeInputs(inputDefinition);
+            propertyDefinition.setConstraints(inputDefinition.getConstraints());
         }
         return propertyDefinition;
     }