Support unknown data types in service import
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / DataTypeImportManager.java
index aa5cb4e..60fdc0b 100644 (file)
@@ -7,9 +7,9 @@
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
  * You may obtain a copy of the License at
- * 
+ *
  *      http://www.apache.org/licenses/LICENSE-2.0
- * 
+ *
  * Unless required by applicable law or agreed to in writing, software
  * distributed under the License is distributed on an "AS IS" BASIS,
  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 package org.openecomp.sdc.be.components.impl;
 
 import fj.data.Either;
+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 javax.annotation.Resource;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.ImmutablePair;
-import org.openecomp.sdc.be.components.impl.CommonImportManager.ElementTypeEnum;
-import org.openecomp.sdc.be.components.impl.ImportUtils.ToscaTagNamesEnum;
 import org.openecomp.sdc.be.dao.api.ActionStatus;
+import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
 import org.openecomp.sdc.be.impl.ComponentsUtils;
 import org.openecomp.sdc.be.model.DataTypeDefinition;
+import org.openecomp.sdc.be.model.Model;
 import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.openecomp.sdc.be.model.normatives.ElementTypeEnum;
 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
+import org.openecomp.sdc.be.model.operations.impl.ModelOperation;
 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
+import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
+import org.openecomp.sdc.be.utils.TypeUtils;
+import org.openecomp.sdc.common.log.wrappers.Logger;
 import org.openecomp.sdc.exception.ResponseFormat;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 import org.springframework.stereotype.Component;
 
-import javax.annotation.Resource;
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.function.Consumer;
-import java.util.stream.Collectors;
-
 @Component("dataTypeImportManager")
 public class DataTypeImportManager {
 
-    public static void main(String[] args) {
-
-        List<PropertyDefinition> properties = new ArrayList<>();
-        PropertyDefinition propertyDefintion = new PropertyDefinition();
-        propertyDefintion.setName("aaa");
-        properties.add(propertyDefintion);
-
-        List<String> allParentsProps = new ArrayList<>();
-        allParentsProps.add("aaa");
-        allParentsProps.add("bbb");
-
-        Set<String> alreadyExistPropsCollection = properties.stream().filter(p -> allParentsProps.contains(p.getName())).map(p -> p.getName()).collect(Collectors.toSet());
-        System.out.println(alreadyExistPropsCollection);
-
-    }
-
-    private static final Logger log = LoggerFactory.getLogger(DataTypeImportManager.class);
+    private static final Logger log = Logger.getLogger(DataTypeImportManager.class.getName());
     @Resource
     private PropertyOperation propertyOperation;
     @Resource
     private ComponentsUtils componentsUtils;
     @Resource
     private CommonImportManager commonImportManager;
+    @Resource
+    private ModelOperation modelOperation;
 
-    public Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> createDataTypes(String dataTypeYml) {
-        return commonImportManager.createElementTypes(dataTypeYml, elementTypeYml -> createDataTypesFromYml(elementTypeYml), elementTypesList -> createDataTypesByDao(elementTypesList), ElementTypeEnum.DataType);
+    public Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> createDataTypes(final String dataTypeYml, final String modelName,
+                                                                                                    final boolean includeToModelDefaultImports) {
+        final var elementTypes = commonImportManager.createElementTypes(
+            dataTypeYml, dataTypesFromYml -> createDataTypesFromYml(dataTypeYml, modelName), this::createDataTypesByDao, ElementTypeEnum.DATA_TYPE);
+
+        if (includeToModelDefaultImports && StringUtils.isNotEmpty(modelName)) {
+            commonImportManager.addTypesToDefaultImports(ElementTypeEnum.DATA_TYPE, dataTypeYml, modelName);
+        }
+        return elementTypes;
     }
 
-    private Either<List<DataTypeDefinition>, ActionStatus> createDataTypesFromYml(String dataTypesYml) {
+    private Either<List<DataTypeDefinition>, ActionStatus> createDataTypesFromYml(final String dataTypesYml, final String modelName) {
+        final Either<List<DataTypeDefinition>, ActionStatus> dataTypesEither = commonImportManager.createElementTypesFromYml(dataTypesYml,
+            this::createDataType);
+        if (dataTypesEither.isRight()) {
+            return dataTypesEither;
+        }
+        final List<DataTypeDefinition> dataTypes = dataTypesEither.left().value();
+        if (StringUtils.isNotEmpty(modelName)) {
+            final Optional<Model> modelOptional = modelOperation.findModelByName(modelName);
+            if (modelOptional.isPresent()) {
+                dataTypes.forEach(dataType -> dataType.setModel(modelName));
+            } else {
+                return Either.right(ActionStatus.INVALID_MODEL);
+            }
+        }
+        if (log.isTraceEnabled()) {
+            log.trace("Unsorted datatypes order:");
+            dataTypes.stream().forEach(dt -> log.trace(dt.getName()));
+        }
 
-        return commonImportManager.createElementTypesFromYml(dataTypesYml, (dataTypeName, dataTypeJsonData) -> createDataType(dataTypeName, dataTypeJsonData));
+        long startTime = System.currentTimeMillis();
+        List<DataTypeDefinition> sortedDataTypeDefinitions = sortDataTypesByDependencyOrder(dataTypes);
 
+        if (log.isTraceEnabled()) {
+            long sortTime = System.currentTimeMillis() - startTime;
+            log.trace("Sorting " + sortedDataTypeDefinitions.size() + " data types from model: " + modelName + " took: " + sortTime);
+            log.trace("Sorted datatypes order:");
+            sortedDataTypeDefinitions.stream().forEach(dt -> log.trace(dt.getName()));
+        }
+        return Either.left(sortedDataTypeDefinitions);
+    }
+    
+    private List<DataTypeDefinition> sortDataTypesByDependencyOrder(final List<DataTypeDefinition> dataTypes) {
+        final List<DataTypeDefinition> sortedDataTypeDefinitions = new ArrayList<>();
+        final Map<String, DataTypeDefinition> dataTypeDefinitionsMap = new HashMap<>();
+        
+        dataTypes.forEach(dataType -> {
+            
+            int highestDependencyIndex = -1;
+            for (final String dependencyName : getDependencyTypes(dataType)) {
+                final DataTypeDefinition dependency = dataTypeDefinitionsMap.get(dependencyName);
+                final int indexOfDependency = sortedDataTypeDefinitions.lastIndexOf(dependency);
+                highestDependencyIndex = indexOfDependency > highestDependencyIndex ? indexOfDependency : highestDependencyIndex;
+            }
+            sortedDataTypeDefinitions.add(highestDependencyIndex + 1, dataType);
+            dataTypeDefinitionsMap.put(dataType.getName(), dataType);
+       
+            } );
+        
+        return sortedDataTypeDefinitions;
+    }
+    
+    private Collection<String> getDependencyTypes(final DataTypeDefinition dataType) {
+        final Set<String> dependencies = new HashSet<>();
+        if (dataType.getDerivedFromName() != null) {
+            dependencies.add(dataType.getDerivedFromName());
+        }
+        if (dataType.getProperties() != null) {
+            dataType.getProperties().stream().forEach(property -> dependencies.add(property.getType()));
+        }
+        return dependencies;
     }
 
-    private Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> createDataTypesByDao(List<DataTypeDefinition> dataTypesToCreate) {
-
-        return commonImportManager.createElementTypesByDao(dataTypesToCreate, dataType -> validateDataType(dataType), dataType -> new ImmutablePair<>(ElementTypeEnum.DataType, dataType.getName()),
-                dataTypeName -> propertyOperation.getDataTypeByNameWithoutDerived(dataTypeName), dataType -> propertyOperation.addDataType(dataType), (newDataType, oldDataType) -> propertyOperation.updateDataType(newDataType, oldDataType));
+    private Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> createDataTypesByDao(
+        List<DataTypeDefinition> dataTypesToCreate) {
+        return commonImportManager.createElementTypesByDao(dataTypesToCreate, this::validateDataType,
+            dataType -> new ImmutablePair<>(ElementTypeEnum.DATA_TYPE, UniqueIdBuilder.buildDataTypeUid(dataType.getModel(), dataType.getName())),
+            dataTypeUid -> propertyOperation.getDataTypeByUidWithoutDerived(dataTypeUid, true),
+            dataType -> propertyOperation.addDataType(dataType),
+            (newDataType, oldDataType) -> propertyOperation.updateDataType(newDataType, oldDataType));
     }
 
     private Either<ActionStatus, ResponseFormat> validateDataType(DataTypeDefinition dataType) {
-
         String dataTypeName = dataType.getName();
         List<PropertyDefinition> properties = dataType.getProperties();
         if (properties == null) {
             // At least one parameter should be defined either in the properties
+
             // section or at one of the parents
             String derivedDataType = dataType.getDerivedFromName();
             // If there are no properties, then we can create a data type if it
+
             // is an abstract one or it derives from non abstract data type
             if (derivedDataType == null || derivedDataType.isEmpty()) {
                 if (!isAbstract(dataType.getName()) && !ToscaPropertyType.isScalarType(dataTypeName)) {
                     log.debug("Data type {} must have properties unless it derives from non abstract data type", dataType.getName());
-                    ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM, dataType, null);
+                    ResponseFormat responseFormat = componentsUtils
+                        .getResponseFormatByDataType(ActionStatus.DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM, dataType, null);
                     return Either.right(responseFormat);
                 }
             } else {
-                // if it is not a scalar data type and it derives from abstract
-                // data type, we should reject the request.
                 if (!ToscaPropertyType.isScalarType(dataTypeName) && isAbstract(derivedDataType)) {
-                    log.debug("Data type {} which derived from abstract data type must have at least one property", dataType.getName());
-                    ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_NOR_PROPERTIES_NEITHER_DERIVED_FROM, dataType, null);
-                    return Either.right(responseFormat);
+                    log.warn("Creating data type {} which derived from abstract data type with no properties", dataType.getName());
                 }
             }
         } else {
             // properties tag cannot be empty
             if (properties.isEmpty()) {
-                ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY, dataType, null);
-
+                ResponseFormat responseFormat = componentsUtils
+                    .getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROPERTIES_CANNOT_BE_EMPTY, dataType, null);
                 return Either.right(responseFormat);
             }
-
             // check no duplicates
-            Set<String> collect = properties.stream().map(p -> p.getName()).collect(Collectors.toSet());
-            if (collect != null) {
-                if (properties.size() != collect.size()) {
-                    ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_DUPLICATE_PROPERTY, dataType, null);
-
-                    return Either.right(responseFormat);
-                }
+            Set<String> collect = properties.stream().map(PropertyDataDefinition::getName).collect(Collectors.toSet());
+            if (collect != null && properties.size() != collect.size()) {
+                ResponseFormat responseFormat = componentsUtils
+                    .getResponseFormatByDataType(ActionStatus.DATA_TYPE_DUPLICATE_PROPERTY, dataType, null);
+                return Either.right(responseFormat);
             }
-
-            List<String> propertiesWithSameTypeAsDataType = properties.stream().filter(p -> p.getType().equals(dataType.getName())).map(p -> p.getName()).collect(Collectors.toList());
-            if (propertiesWithSameTypeAsDataType != null && propertiesWithSameTypeAsDataType.isEmpty() == false) {
+            List<String> propertiesWithSameTypeAsDataType = properties.stream().filter(p -> p.getType().equals(dataType.getName()))
+                .map(PropertyDataDefinition::getName).collect(Collectors.toList());
+            if (propertiesWithSameTypeAsDataType != null && !propertiesWithSameTypeAsDataType.isEmpty()) {
                 log.debug("The data type {} contains properties with the type {}", dataType.getName(), dataType.getName());
-                ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE, dataType, propertiesWithSameTypeAsDataType);
-
+                ResponseFormat responseFormat = componentsUtils
+                    .getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROEPRTY_CANNOT_HAVE_SAME_TYPE_OF_DATA_TYPE, dataType,
+                        propertiesWithSameTypeAsDataType);
                 return Either.right(responseFormat);
             }
         }
-
         String derivedDataType = dataType.getDerivedFromName();
         if (derivedDataType != null) {
-            Either<DataTypeDefinition, StorageOperationStatus> derivedDataTypeByName = propertyOperation.getDataTypeByName(derivedDataType, true);
+            Either<DataTypeDefinition, StorageOperationStatus> derivedDataTypeByName = propertyOperation.getDataTypeByName(derivedDataType,
+                dataType.getModel());
             if (derivedDataTypeByName.isRight()) {
                 StorageOperationStatus status = derivedDataTypeByName.right().value();
                 if (status == StorageOperationStatus.NOT_FOUND) {
-                    ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_DERIVED_IS_MISSING, dataType, null);
-
+                    ResponseFormat responseFormat = componentsUtils
+                        .getResponseFormatByDataType(ActionStatus.DATA_TYPE_DERIVED_IS_MISSING, dataType, null);
                     return Either.right(responseFormat);
                 } else {
                     ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.GENERAL_ERROR, dataType, null);
-
                     return Either.right(responseFormat);
-
                 }
             } else {
-
                 DataTypeDefinition derivedDataTypeDef = derivedDataTypeByName.left().value();
-                if (properties != null && properties.isEmpty() == false) {
-
-                    if (true == isScalarType(derivedDataTypeDef)) {
-                        ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_CANNOT_HAVE_PROPERTIES, dataType, null);
-
+                if (properties != null && !properties.isEmpty() && derivedDataTypeDef != null) {
+                    if (isScalarType(derivedDataTypeDef)) {
+                        ResponseFormat responseFormat = componentsUtils
+                            .getResponseFormatByDataType(ActionStatus.DATA_TYPE_CANNOT_HAVE_PROPERTIES, dataType, null);
                         return Either.right(responseFormat);
                     }
-
                     Set<String> allParentsProps = new HashSet<>();
                     do {
                         List<PropertyDefinition> currentParentsProps = derivedDataTypeDef.getProperties();
@@ -176,18 +225,18 @@ public class DataTypeImportManager {
                         }
                         derivedDataTypeDef = derivedDataTypeDef.getDerivedFrom();
                     } while (derivedDataTypeDef != null);
-
                     // Check that no property is already defined in one of the
+
                     // ancestors
-                    Set<String> alreadyExistPropsCollection = properties.stream().filter(p -> allParentsProps.contains(p.getName())).map(p -> p.getName()).collect(Collectors.toSet());
-                    if (alreadyExistPropsCollection != null && alreadyExistPropsCollection.isEmpty() == false) {
+                    Set<String> alreadyExistPropsCollection = properties.stream().filter(p -> allParentsProps.contains(p.getName()))
+                        .map(PropertyDataDefinition::getName).collect(Collectors.toSet());
+                    if (alreadyExistPropsCollection != null && !alreadyExistPropsCollection.isEmpty()) {
                         List<String> duplicateProps = new ArrayList<>();
                         duplicateProps.addAll(alreadyExistPropsCollection);
-                        ResponseFormat responseFormat = componentsUtils.getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR, dataType, duplicateProps);
-
+                        ResponseFormat responseFormat = componentsUtils
+                            .getResponseFormatByDataType(ActionStatus.DATA_TYPE_PROPERTY_ALREADY_DEFINED_IN_ANCESTOR, dataType, duplicateProps);
                         return Either.right(responseFormat);
                     }
-
                 }
             }
         }
@@ -195,55 +244,35 @@ public class DataTypeImportManager {
     }
 
     private boolean isAbstract(String dataTypeName) {
-
         ToscaPropertyType isPrimitiveToscaType = ToscaPropertyType.isValidType(dataTypeName);
-
-        return isPrimitiveToscaType != null && isPrimitiveToscaType.isAbstract() == true;
-
+        return isPrimitiveToscaType != null && isPrimitiveToscaType.isAbstract();
     }
 
     private boolean isScalarType(DataTypeDefinition dataTypeDef) {
-
         boolean isScalar = false;
         DataTypeDefinition dataType = dataTypeDef;
-
         while (dataType != null) {
-
             String name = dataType.getName();
             if (ToscaPropertyType.isScalarType(name)) {
                 isScalar = true;
                 break;
             }
-
             dataType = dataType.getDerivedFrom();
         }
-
         return isScalar;
     }
 
     private DataTypeDefinition createDataType(String dataTypeName, Map<String, Object> toscaJson) {
         DataTypeDefinition dataType = new DataTypeDefinition();
-
         dataType.setName(dataTypeName);
-
         if (toscaJson != null) {
             // Description
-            final Consumer<String> descriptionSetter = description -> dataType.setDescription(description);
-            commonImportManager.setField(toscaJson, ToscaTagNamesEnum.DESCRIPTION.getElementName(), descriptionSetter);
+            commonImportManager.setField(toscaJson, TypeUtils.ToscaTagNamesEnum.DESCRIPTION.getElementName(), dataType::setDescription);
             // Derived From
-            final Consumer<String> derivedFromSetter = derivedFrom -> dataType.setDerivedFromName(derivedFrom);
-            commonImportManager.setField(toscaJson, ToscaTagNamesEnum.DERIVED_FROM.getElementName(), derivedFromSetter);
+            commonImportManager.setField(toscaJson, TypeUtils.ToscaTagNamesEnum.DERIVED_FROM.getElementName(), dataType::setDerivedFromName);
             // Properties
-            commonImportManager.setProperties(toscaJson, (values) -> dataType.setProperties(values));
-
-            setConstraints(toscaJson, dataType);
+            CommonImportManager.setProperties(toscaJson, dataType::setProperties);
         }
         return dataType;
     }
-
-    private void setConstraints(Map<String, Object> toscaJson, DataTypeDefinition dataType) {
-        // TODO Auto-generated method stub
-
-    }
-
 }