Support updated data types in service import 20/130420/8
authorMichaelMorris <michael.morris@est.tech>
Fri, 19 Aug 2022 08:36:41 +0000 (09:36 +0100)
committerVasyl Razinkov <vasyl.razinkov@est.tech>
Fri, 2 Sep 2022 10:22:01 +0000 (10:22 +0000)
Signed-off-by: MichaelMorris <michael.morris@est.tech>
Issue-ID: SDC-4140
Change-Id: Ib66d47a0f566b648722ce86cfc4e208880551a29

15 files changed:
catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/CommonImportManager.java
catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/DataTypeImportManager.java
catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceImportBusinessLogic.java
catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ServiceImportBusinessLogicTest.java
catalog-be/src/test/resources/csars/service-Ser09080002-csar.csar
catalog-model/src/main/java/org/openecomp/sdc/be/model/DataTypeDefinition.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/cache/ApplicationDataTypeCache.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/PropertyOperation.java
catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java
catalog-model/src/test/resources/modelOperation/expected-additional_types-1.yaml
catalog-model/src/test/resources/modelOperation/expected-additional_types-2-updated.yaml [new file with mode: 0644]
catalog-model/src/test/resources/modelOperation/expected-additional_types-2.yaml
catalog-model/src/test/resources/modelOperation/input-data_types-updated.yaml [new file with mode: 0644]
catalog-model/src/test/resources/modelOperation/input-data_types.yaml

index f1582ee..6bbeed0 100644 (file)
@@ -538,9 +538,17 @@ public class CommonImportManager {
     public void addTypesToDefaultImports(final ElementTypeEnum elementTypeEnum, final String typesYaml, final String modelName) {
         modelOperation.addTypesToDefaultImports(elementTypeEnum, typesYaml, modelName);
     }
+    
+    public void updateTypesInAdditionalTypesImport(final ElementTypeEnum elementTypeEnum, final String dataTypeYml, final String modelName) {
+        modelOperation.updateTypesInAdditionalTypesImport(elementTypeEnum, dataTypeYml, modelName);
+    }
 
     public interface ICreateElementType<T1, T2, T3> {
 
         T3 createElement(T1 firstArg, T2 secondArg);
     }
+
+
+
+
 }
index 943edb1..d488c65 100644 (file)
@@ -65,12 +65,15 @@ public class DataTypeImportManager {
 
     public Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> createDataTypes(final String dataTypeYml, final String modelName,
                                                                                                     final boolean includeToModelDefaultImports) {
-        final var elementTypes = commonImportManager.createElementTypes(
+        final Either<List<ImmutablePair<DataTypeDefinition, Boolean>>, ResponseFormat> 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);
         }
+        if (!includeToModelDefaultImports && StringUtils.isNotEmpty(modelName) && elementTypes.isLeft()) {
+            commonImportManager.updateTypesInAdditionalTypesImport(ElementTypeEnum.DATA_TYPE, dataTypeYml, modelName);
+        }
         return elementTypes;
     }
 
index a3e3a6b..b4079ac 100644 (file)
@@ -250,6 +250,9 @@ public class ServiceImportBusinessLogic {
             final Map<String, Object> dataTypesToCreate = getDatatypesToCreate(service.getModel(), csarInfo);
             if (MapUtils.isNotEmpty(dataTypesToCreate)) {
                 dataTypeBusinessLogic.createDataTypeFromYaml(new Yaml().dump(dataTypesToCreate), service.getModel(), true);
+                dataTypesToCreate.entrySet().stream().forEach(createdOrUpdatedDataType -> {
+                    applicationDataTypeCache.reload(service.getModel(), UniqueIdBuilder.buildDataTypeUid(service.getModel(), createdOrUpdatedDataType.getKey()));
+                });
             }
             final List<NodeTypeDefinition> nodeTypesToCreate = getNodeTypesToCreate(service.getModel(), csarInfo);
             if (CollectionUtils.isNotEmpty(nodeTypesToCreate)) {
@@ -283,9 +286,18 @@ public class ServiceImportBusinessLogic {
                 dataTypesToCreate.put(dataTypeEntry.getKey(), dataTypeEntry.getValue());
                 log.info("Deploying unknown type " + dataTypeEntry.getKey() + " to model " + model + " from package " + csarInfo.getCsarUUID());
             }
+            if (hasNewProperties(result, (Map<String, Map<String, Object>>) dataTypeEntry.getValue())) {
+                dataTypesToCreate.put(dataTypeEntry.getKey(), dataTypeEntry.getValue());
+                log.info("Deploying new version of type " + dataTypeEntry.getKey() + " to model " + model + " from package " + csarInfo.getCsarUUID());
+            }
         }
         return dataTypesToCreate;
     }
+    
+    private boolean hasNewProperties(final Either<DataTypeDefinition, JanusGraphOperationStatus> result, final Map<String, Map<String, Object>> dataType) {
+        return result.isLeft() && dataType.containsKey("properties") && result.left().value().getProperties() != null
+                && result.left().value().getProperties().size() != dataType.get("properties").size();
+    }
 
     private void createNodeTypes(List<NodeTypeDefinition> nodeTypesToCreate, ServiceCsarInfo csarInfo) {
         NodeTypesMetadataList nodeTypesMetadataList = new NodeTypesMetadataList();
index 07593a0..3f8f9c3 100644 (file)
@@ -218,7 +218,21 @@ class ServiceImportBusinessLogicTest extends ServiceImportBussinessLogicBaseTest
         when(toscaOperationFacade.updateInputsToComponent(anyList(), eq(newService.getUniqueId()))).thenReturn(Either.left(new ArrayList<>()));
 
         when(applicationDataTypeCache.get(any(), contains("tosca.datatypes.test_"))).thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
-        when(applicationDataTypeCache.get(any(), matches("^((?!tosca.datatypes.test_).)*$"))).thenReturn(Either.left(new DataTypeDefinition()));
+        DataTypeDefinition typeToBeUpdated = new DataTypeDefinition();
+        List<PropertyDefinition> properties = new ArrayList<>();
+        PropertyDefinition nameProperty = new PropertyDefinition();
+        nameProperty.setName("name");
+        nameProperty.setType("string");
+        properties.add(nameProperty);
+        PropertyDefinition versionProperty = new PropertyDefinition();
+        versionProperty.setName("version");
+        versionProperty.setType("string");
+        properties.add(versionProperty);
+        typeToBeUpdated.setProperties(properties);
+        when(applicationDataTypeCache.get(any(), eq("onap.datatypes.ToscaConceptIdentifier.datatype"))).thenReturn(Either.left(typeToBeUpdated));
+        when(applicationDataTypeCache.get(any(), matches("^((?!(tosca.datatypes.test_|onap.datatypes.ToscaConceptIdentifier)).)*$"))).thenReturn(Either.left(new DataTypeDefinition()));
+
+
 
         when(toscaOperationFacade.getLatestByToscaResourceName(contains("org.openecomp.resource"), isNull())).thenReturn(Either.left(null));
         when(toscaOperationFacade.getLatestByToscaResourceName(contains("tosca.nodes."), isNull())).thenReturn(Either.left(null));
@@ -240,9 +254,10 @@ class ServiceImportBusinessLogicTest extends ServiceImportBussinessLogicBaseTest
         ArgumentCaptor<String> yaml = ArgumentCaptor.forClass(String.class);
         verify(dataTypeBusinessLogic).createDataTypeFromYaml(yaml.capture(), isNull(), anyBoolean());
         Map<String, Object> yamlMap = new Yaml().load(yaml.getValue());
-        assertEquals(2, yamlMap.size());
+        assertEquals(3, yamlMap.size());
         assertNotNull(yamlMap.get("tosca.datatypes.test_a"));
         assertNotNull(yamlMap.get("tosca.datatypes.test_b"));
+        assertNotNull(yamlMap.get("onap.datatypes.ToscaConceptIdentifier"));
     }
 
     @Test
index a658504..00a6981 100644 (file)
Binary files a/catalog-be/src/test/resources/csars/service-Ser09080002-csar.csar and b/catalog-be/src/test/resources/csars/service-Ser09080002-csar.csar differ
index bf9f4d7..f208891 100644 (file)
@@ -50,6 +50,7 @@ public class DataTypeDefinition extends DataTypeDataDefinition {
         this.setConstraints(dataTypeDefinition.getConstraints());
         this.setDescription(dataTypeDefinition.getDescription());
         this.setModel(dataTypeDefinition.getModel());
+        this.setProperties(dataTypeDefinition.getProperties());
     }
 
     public List<PropertyConstraint> safeGetConstraints() {
index 0b4d02a..338bbe1 100644 (file)
@@ -198,6 +198,21 @@ public class ApplicationDataTypeCache implements ApplicationCache<DataTypeDefini
             readWriteLock.readLock().unlock();
         }
     }
+    
+    public void reload(final String model, final String uniqueId) {
+        final Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeDefEither = propertyOperation.getDataTypeByUid(uniqueId);
+        if (dataTypeDefEither.isLeft()) {
+            DataTypeDefinition dataTypeDef = dataTypeDefEither.left().value();
+            if (getDataTypeDefinitionMapByModel(model).containsKey(dataTypeDef.getName())) {
+                try {
+                    readWriteLock.readLock().lock();
+                    getDataTypeDefinitionMapByModel(model).put(dataTypeDef.getName(), dataTypeDef);
+                } finally {
+                    readWriteLock.readLock().unlock();
+                }
+            }
+        }
+    }
 
     private Map<String, DataTypeDefinition> getDataTypeDefinitionMapByModel(final String model) {
         return dataTypesByModelCacheMap.containsKey(model) ? dataTypesByModelCacheMap.get(model) : new HashMap<>();
index 01e5cdc..0d462c9 100644 (file)
@@ -26,12 +26,16 @@ import java.nio.file.Path;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.EnumMap;
+import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.Set;
 import java.util.stream.Collectors;
+import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.ImmutablePair;
@@ -50,6 +54,7 @@ import org.openecomp.sdc.be.data.model.ToscaImportByModel;
 import org.openecomp.sdc.be.datatypes.enums.GraphPropertyEnum;
 import org.openecomp.sdc.be.datatypes.enums.ModelTypeEnum;
 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
+import org.openecomp.sdc.be.model.DataTypeDefinition;
 import org.openecomp.sdc.be.model.Model;
 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.ModelOperationExceptionSupplier;
 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException;
@@ -379,4 +384,50 @@ public class ModelOperation {
         this.modelElementOperation = modelElementOperation;
     }
 
+    @SuppressWarnings("unchecked")
+    public void updateTypesInAdditionalTypesImport(final ElementTypeEnum elementTypeEnum, final String typesYaml, final String modelName) {
+        final Optional<ToscaImportByModel> additionalTypeDefinitionsImportOptional = getAdditionalTypes(modelName);
+
+        if (additionalTypeDefinitionsImportOptional.isPresent()) {
+
+            final Map<String, Object> existingTypeContent = getExistingTypes(elementTypeEnum, additionalTypeDefinitionsImportOptional.get());
+            final Set<String> existingTypeNames = existingTypeContent.keySet();
+
+            final Map<String, Object> typesToUpate = new HashMap<>();
+
+            final Map<String, Object> newTypesYaml = new Yaml().load(typesYaml);
+            newTypesYaml.entrySet().stream().filter(entry -> existingTypeNames.contains(entry.getKey())).forEach(newTypeToUpdate -> {
+
+                final Map<String, Object> propertiesInNewDef = (Map<String, Object>) ((Map<String, Object>) newTypeToUpdate.getValue()).get("properties");
+                final Map<String, Object> existingProperties =
+                        (Map<String, Object>) ((Map<String, Object>) existingTypeContent.get(newTypeToUpdate.getKey())).get("properties");
+
+                final List<Entry<String, Object>> propertiesMissingFromNewDef = MapUtils.isEmpty(existingProperties) ? Collections.emptyList()
+                        : existingProperties.entrySet().stream()
+                                .filter(existingPropEntry -> !propertiesInNewDef.keySet().contains(existingPropEntry.getKey()))
+                                .collect(Collectors.toList());
+
+                if (CollectionUtils.isNotEmpty(propertiesMissingFromNewDef)) {
+                    typesToUpate.put(newTypeToUpdate.getKey(), newTypeToUpdate.getValue());
+
+                    propertiesMissingFromNewDef
+                            .forEach(existingPropToAdd -> propertiesInNewDef.put(existingPropToAdd.getKey(), existingPropToAdd.getValue()));
+                }
+            });
+            if (MapUtils.isNotEmpty(typesToUpate)) {
+                addTypesToDefaultImports(elementTypeEnum, new Yaml().dumpAsMap(typesToUpate), modelName);
+            }
+        }
+    }
+    
+    private  Optional<ToscaImportByModel> getAdditionalTypes(final String modelName) {
+        final List<ToscaImportByModel> modelImportList = toscaModelImportCassandraDao.findAllByModel(modelName);
+        return modelImportList.stream().filter(t -> ADDITIONAL_TYPE_DEFINITIONS_PATH.equals(Path.of(t.getFullPath()))).findAny();
+    }
+    
+    private Map<String, Object> getExistingTypes(final ElementTypeEnum elementTypeEnum, final ToscaImportByModel additionalTypeDefinitionsImport) {
+        final Map<String, Object> existingContent = new Yaml().load(additionalTypeDefinitionsImport.getContent());
+        return  (Map<String, Object>) existingContent.get(elementTypeEnum.getToscaEntryName());
+    }
+
 }
index 7546e90..b5215dc 100644 (file)
@@ -1822,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;
@@ -1935,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);
index 42f5298..acff2a3 100644 (file)
@@ -31,6 +31,7 @@ import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 import static org.openecomp.sdc.be.model.operations.impl.ModelOperation.ADDITIONAL_TYPE_DEFINITIONS_PATH;
@@ -503,7 +504,28 @@ class ModelOperationTest extends ModelTestBase {
         final ToscaImportByModel actualImport1 = actualImportList.stream().filter(expectedImport1::equals).findFirst().orElse(null);
         assertNotNull(actualImport1);
         assertEquals(expectedImport1.getContent(), actualImport1.getContent());
-
+        
+        // Update the added additional type
+        final var updatedDataTypesPath = testResourcePath.resolve(Path.of("input-data_types-updated.yaml"));
+        final var updatedDataTypes = Files.readString(updatedDataTypesPath);
+        modelOperation.updateTypesInAdditionalTypesImport(ElementTypeEnum.DATA_TYPE, updatedDataTypes, modelName);
+        
+        ArgumentCaptor<List<ToscaImportByModel>> updatedImportListArgumentCaptor = ArgumentCaptor.forClass(List.class);
+        verify(toscaModelImportCassandraDao, times(2)).saveAll(eq(modelName), updatedImportListArgumentCaptor.capture());
+
+        final List<ToscaImportByModel> updatedActualImportList = updatedImportListArgumentCaptor.getValue();
+        assertEquals(2, updatedActualImportList.size());
+        
+        var expectedUpdatedAdditionalTypesImport = new ToscaImportByModel();
+        expectedUpdatedAdditionalTypesImport.setModelId(modelName);
+        expectedUpdatedAdditionalTypesImport.setFullPath(ADDITIONAL_TYPE_DEFINITIONS_PATH.toString());
+        expectedUpdatedAdditionalTypesImport.setContent(Files.readString(testResourcePath.resolve(Path.of("expected-additional_types-2-updated.yaml"))));
+        final ToscaImportByModel actualUpdatedAdditionalTypesImport =
+            actualImportList.stream().filter(expectedUpdatedAdditionalTypesImport::equals).findFirst().orElse(null);
+        assertNotNull(actualUpdatedAdditionalTypesImport);
+        
+        assertTrue(actualUpdatedAdditionalTypesImport.getContent().contains("added_property_1"));
+        assertTrue(actualUpdatedAdditionalTypesImport.getContent().contains("added_property_2"));
     }
 
     @Test
index c9e6741..50d9bab 100644 (file)
@@ -5,6 +5,9 @@ data_types:
   tosca.datatypes.nfv.ServiceAvailability:
     derived_from: tosca.datatypes.Root
     description: additional type
+    properties:
+      added_property_1:
+        type: string
   tosca.datatypes.nfv.L2AddressData:
     derived_from: tosca.datatypes.Root
     description: additional type
diff --git a/catalog-model/src/test/resources/modelOperation/expected-additional_types-2-updated.yaml b/catalog-model/src/test/resources/modelOperation/expected-additional_types-2-updated.yaml
new file mode 100644 (file)
index 0000000..ad3d0bf
--- /dev/null
@@ -0,0 +1,24 @@
+tosca_definitions_version: tosca_simple_yaml_1_3
+description: Auto-generated file that contains package custom types or types added
+  after system installation.
+data_types:
+  tosca.datatypes.nfv.PreviouslyExistingType1:
+    derived_from: tosca.datatypes.Root
+    description: additional type
+  tosca.datatypes.nfv.PreviouslyExistingType2:
+    derived_from: tosca.datatypes.Root
+    description: additional type
+  tosca.datatypes.nfv.ServiceAvailability:
+    derived_from: tosca.datatypes.Root
+    description: additional type
+    properties:
+      added_property_1:
+        type: string
+      added_property_2:
+        type: string
+  tosca.datatypes.nfv.L2AddressData:
+    derived_from: tosca.datatypes.Root
+    description: additional type
+  tosca.datatypes.nfv.Unknown:
+    derived_from: tosca.datatypes.Root
+    description: additional type
index 2d2c542..a36c828 100644 (file)
@@ -11,6 +11,9 @@ data_types:
   tosca.datatypes.nfv.ServiceAvailability:
     derived_from: tosca.datatypes.Root
     description: additional type
+    properties:
+      added_property_1:
+        type: string
   tosca.datatypes.nfv.L2AddressData:
     derived_from: tosca.datatypes.Root
     description: additional type
diff --git a/catalog-model/src/test/resources/modelOperation/input-data_types-updated.yaml b/catalog-model/src/test/resources/modelOperation/input-data_types-updated.yaml
new file mode 100644 (file)
index 0000000..136585a
--- /dev/null
@@ -0,0 +1,12 @@
+tosca.datatypes.nfv.ServiceAvailability:
+  derived_from: tosca.datatypes.Root
+  description: additional type
+  properties:
+    added_property_2:
+      type: string
+tosca.datatypes.nfv.L2AddressData:
+  derived_from: tosca.datatypes.Root
+  description: additional type
+tosca.datatypes.nfv.Unknown:
+  derived_from: tosca.datatypes.Root
+  description: additional type
\ No newline at end of file
index 77b7d97..0754631 100644 (file)
@@ -1,6 +1,9 @@
 tosca.datatypes.nfv.ServiceAvailability:
   derived_from: tosca.datatypes.Root
   description: additional type
+  properties:
+    added_property_1:
+      type: string
 tosca.datatypes.nfv.L2AddressData:
   derived_from: tosca.datatypes.Root
   description: additional type