Add support for update to artifact types endpoint 15/126615/8
authorfranciscovila <javier.paradela.vila@est.tech>
Mon, 17 Jan 2022 10:41:23 +0000 (10:41 +0000)
committerMichael Morris <michael.morris@est.tech>
Fri, 21 Jan 2022 14:13:52 +0000 (14:13 +0000)
Creates a new artifact type or overwrites it,
if this already exists.

Issue-ID: SDC-3846
Signed-off-by: franciscovila <javier.paradela.vila@est.tech>
Change-Id: Ia380dd4d790a94aa030ccf434de0440cc620f55c

catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ArtifactTypeOperation.java
catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ArtifactTypeOperationTest.java [new file with mode: 0644]

index d8a78cd..d16a735 100644 (file)
 package org.openecomp.sdc.be.model.operations.impl;
 
 import fj.data.Either;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.stream.Collectors;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.openecomp.sdc.be.dao.api.ActionStatus;
 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
 import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
 import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
-import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao;
 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
 import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
+import org.openecomp.sdc.be.datatypes.elements.ArtifactTypeDataDefinition;
 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
 import org.openecomp.sdc.be.model.ArtifactTypeDefinition;
 import org.openecomp.sdc.be.model.PropertyDefinition;
@@ -54,25 +47,26 @@ import org.openecomp.sdc.common.log.wrappers.Logger;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 
+import java.util.*;
+import java.util.stream.Collectors;
+
+
 /**
  * This class is responsible for handling all operations for the TOSCA artifact types
  */
 @Component("artifact-type-operation")
-public class ArtifactTypeOperation implements IArtifactTypeOperation {
+public class ArtifactTypeOperation extends AbstractOperation implements IArtifactTypeOperation {
 
     private static final Logger LOGGER = Logger.getLogger(ArtifactTypeOperation.class.getName());
 
-    private final JanusGraphGenericDao janusGraphGenericDao;
     private final PropertyOperation propertyOperation;
     private final DerivedFromOperation derivedFromOperation;
     private final ModelOperation modelOperation;
 
     @Autowired
-    public ArtifactTypeOperation(final JanusGraphGenericDao janusGraphGenericDao,
-                                 final PropertyOperation propertyOperation,
+    public ArtifactTypeOperation(final PropertyOperation propertyOperation,
                                  final DerivedFromOperation derivedFromOperation,
                                  final ModelOperation modelOperation) {
-        this.janusGraphGenericDao = janusGraphGenericDao;
         this.propertyOperation = propertyOperation;
         this.derivedFromOperation = derivedFromOperation;
         this.modelOperation = modelOperation;
@@ -91,30 +85,39 @@ public class ArtifactTypeOperation implements IArtifactTypeOperation {
     @Override
     public ArtifactTypeDefinition createArtifactType(final ArtifactTypeDefinition artifactType,
                                                      final boolean inTransaction) {
-        Either<ArtifactTypeData, JanusGraphOperationStatus> createNodeResult = null;
+        Either<ArtifactTypeDefinition, StorageOperationStatus> createNodeResult = null;
         try {
             artifactType.setUniqueId(UniqueIdBuilder.buildArtifactTypeUid(artifactType.getModel(), artifactType.getType()));
             final ArtifactTypeData artifactTypeData = new ArtifactTypeData(artifactType);
             final Either<ArtifactTypeData, JanusGraphOperationStatus> existArtifact = janusGraphGenericDao
                 .getNode(artifactTypeData.getUniqueIdKey(), artifactTypeData.getUniqueId(), ArtifactTypeData.class);
             if (!existArtifact.isLeft()) {
-                createNodeResult = janusGraphGenericDao.createNode(artifactTypeData, ArtifactTypeData.class);
-                if (createNodeResult.isRight()) {
-                    final JanusGraphOperationStatus operationStatus = createNodeResult.right().value();
-                    LOGGER.error(EcompLoggerErrorCode.DATA_ERROR,
-                        "Failed to add artifact type {} to graph. Operation status {}", artifactType.getType(), operationStatus);
-                    throw new OperationException(ActionStatus.GENERAL_ERROR,
-                        String.format("Failed to create artifact type %s with status %s", artifactType.getType(),
-                            DaoStatusConverter.convertJanusGraphStatusToStorageStatus(operationStatus)));
-                }
+                LOGGER.debug("Artifact type did not exist, creating new one {}", artifactType);
+                createNodeResult = janusGraphGenericDao.createNode(artifactTypeData, ArtifactTypeData.class).right()
+                        .map(DaoStatusConverter::convertJanusGraphStatusToStorageStatus).left()
+                        .map(createdDerivedFrom -> artifactType);
                 addPropertiesToArtifactType(artifactType);
                 addModelRelation(artifactType);
                 addDerivedFromRelation(artifactType);
-                return convertArtifactDataDefinition(createNodeResult.left().value());
-            } else {
-                LOGGER.debug("Artifact type already exist {}", artifactType);
-                return convertArtifactDataDefinition(existArtifact.left().value());
             }
+            else {
+                LOGGER.debug("Artifact type {} already exists", artifactType);
+                createNodeResult = janusGraphGenericDao.updateNode(artifactTypeData, ArtifactTypeData.class).right()
+                        .map(DaoStatusConverter::convertJanusGraphStatusToStorageStatus).left()
+                        .bind(updatedNode -> updateArtifactProperties(artifactType.getUniqueId(), artifactType.getProperties())).left()
+                        .bind(updatedProperties -> updateArtifactDerivedFrom(artifactType, artifactType.getDerivedFrom())).left()
+                        .map(updatedDerivedFrom -> artifactType);
+            }
+            if (createNodeResult.isRight()) {
+                final StorageOperationStatus operationStatus = createNodeResult.right().value();
+                LOGGER.error(EcompLoggerErrorCode.DATA_ERROR,
+                    "Failed to add artifact type {} to graph. Operation status {}", artifactType.getType(), operationStatus);
+                throw new OperationException(ActionStatus.GENERAL_ERROR,
+                    String.format("Failed to create artifact type %s with status %s", artifactType.getType(),
+                        operationStatus));
+            }
+            return createNodeResult.left().value();
+
         } finally {
             if (!inTransaction) {
                 if (createNodeResult == null || createNodeResult.isRight()) {
@@ -156,6 +159,43 @@ public class ArtifactTypeOperation implements IArtifactTypeOperation {
         return artifactTypeDefinitionTypes;
     }
 
+    private Either<Map<String, PropertyData>, StorageOperationStatus> updateArtifactProperties(final String artifactId, final List<PropertyDefinition> properties) {
+        LOGGER.debug("#updateArtifactProperties - updating artifact type properties for artifact type with id {}", artifactId);
+        return propertyOperation.deletePropertiesAssociatedToNode(NodeTypeEnum.ArtifactType, artifactId).left()
+                .bind(deleteProps -> addPropertiesToArtifact(artifactId, properties));
+    }
+
+    private Either<GraphRelation, StorageOperationStatus> updateArtifactDerivedFrom(ArtifactTypeDefinition updatedArtifactType,
+                                                                                  String currDerivedFromArtifactType) {
+        String artifactTypeId = updatedArtifactType.getUniqueId();
+        LOGGER.debug(
+                "#updateArtifactDerivedFrom - updating artifact derived from relation for artifact type with id {}. old derived type {}. new derived type {}",
+                artifactTypeId, currDerivedFromArtifactType, updatedArtifactType.getDerivedFrom());
+        StorageOperationStatus deleteDerivedRelationStatus = deleteDerivedFromArtifactType(artifactTypeId, currDerivedFromArtifactType, updatedArtifactType.getModel());
+        if (deleteDerivedRelationStatus != StorageOperationStatus.OK) {
+            return Either.right(deleteDerivedRelationStatus);
+        }
+        return addDerivedFromRelation(updatedArtifactType, artifactTypeId);
+    }
+
+    private StorageOperationStatus deleteDerivedFromArtifactType(String artifactTypeId, String derivedFromType, String model) {
+        if (derivedFromType == null) {
+            return StorageOperationStatus.OK;
+        }
+        LOGGER.debug("#deleteDerivedFromArtifactType - deleting derivedFrom relation for artifact type with id {} and its derived type {}", artifactTypeId,
+                derivedFromType);
+        return getProjectionLatestArtifactTypeByType(derivedFromType, model).either(
+                derivedFromNode -> derivedFromOperation.removeDerivedFromRelation(artifactTypeId, derivedFromNode.getUniqueId(), NodeTypeEnum.ArtifactType),
+                err -> err);
+    }
+
+    private Either<Map<String, PropertyData>, StorageOperationStatus> addPropertiesToArtifact(String artifactId,
+                                                                                            List<PropertyDefinition> properties) {
+        LOGGER.debug("#addPropertiesToArtifact - adding artifact type properties for artifact type with id {}", artifactId);
+        return propertyOperation.addPropertiesToElementType(artifactId, NodeTypeEnum.ArtifactType, properties).right()
+                .map(DaoStatusConverter::convertJanusGraphStatusToStorageStatus);
+    }
+
     /**
      * Coverts the given artifact type data into a TOSCA artifact types definition
      * @param artifactTypeData artifact type data representation
@@ -197,6 +237,13 @@ public class ArtifactTypeOperation implements IArtifactTypeOperation {
         return Optional.of(result.left().value().get(0));
     }
 
+    private Either<ArtifactTypeDefinition, StorageOperationStatus> getProjectionLatestArtifactTypeByType(String type, String model) {
+        Map<String, Object> mapCriteria = new HashMap<>();
+        mapCriteria.put(GraphPropertiesDictionary.TYPE.getProperty(), type);
+        mapCriteria.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
+        return getArtifactTypeByCriteria(type, mapCriteria, model);
+    }
+
     /**
      * Creates the derivedFrom relation for the given TOSCA artifact types
      * @param artifactType the TOSCA artifact types definition
@@ -217,6 +264,63 @@ public class ArtifactTypeOperation implements IArtifactTypeOperation {
         }
     }
 
+    private Either<GraphRelation, StorageOperationStatus> addDerivedFromRelation(ArtifactTypeDefinition artifactType, String atUniqueId) {
+        String derivedFrom = artifactType.getDerivedFrom();
+        if (derivedFrom == null) {
+            return Either.left(null);
+        }
+        LOGGER.debug("#addDerivedFromRelationBefore - adding derived from relation between artifact type {} to its parent {}", artifactType.getType(),
+                derivedFrom);
+        return this.getProjectionLatestArtifactTypeByType(derivedFrom, artifactType.getModel()).left().bind(
+                derivedFromArtifact -> derivedFromOperation.addDerivedFromRelation(atUniqueId, derivedFromArtifact.getUniqueId(), NodeTypeEnum.ArtifactType));
+    }
+
+    private Either<ArtifactTypeDefinition, StorageOperationStatus> getArtifactTypeByCriteria(String type, Map<String, Object> properties, String model) {
+        Either<ArtifactTypeDefinition, StorageOperationStatus> result;
+        if (type == null || type.isEmpty()) {
+            LOGGER.error("type is empty");
+            result = Either.right(StorageOperationStatus.INVALID_ID);
+            return result;
+        }
+        Either<List<ArtifactTypeData>, JanusGraphOperationStatus> eitherArtifactData = janusGraphGenericDao
+                .getByCriteriaForModel(NodeTypeEnum.ArtifactType, properties, model, ArtifactTypeData.class);
+        if (eitherArtifactData.isRight()) {
+            result = Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(eitherArtifactData.right().value()));
+        } else {
+            ArtifactTypeDataDefinition dataDefinition = eitherArtifactData.left().value().stream().map(ArtifactTypeData::getArtifactTypeDataDefinition)
+                    .findFirst().get();
+            result = getArtifactTypeByUid(dataDefinition.getUniqueId());
+        }
+        return result;
+    }
+
+    private Either<ArtifactTypeDefinition, StorageOperationStatus> getArtifactTypeByUid(String uniqueId) {
+        LOGGER.debug("#getArtifactTypeByUid - fetching artifact type with id {}", uniqueId);
+        return janusGraphGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactType), uniqueId, ArtifactTypeData.class).right()
+                .map(DaoStatusConverter::convertJanusGraphStatusToStorageStatus).left()
+                .bind(artifactType -> createArtifactTypeDefinition(uniqueId, artifactType));
+    }
+
+    private Either<ArtifactTypeDefinition, StorageOperationStatus> createArtifactTypeDefinition(String uniqueId, ArtifactTypeData artifactTypeNode) {
+        ArtifactTypeDefinition artifactType = new ArtifactTypeDefinition(artifactTypeNode.getArtifactTypeDataDefinition());
+        Optional<String> modelName = getAssociatedModelName(uniqueId);
+        if (modelName.isPresent()) {
+            artifactType.setModel(modelName.get());
+        }
+        return fillDerivedFrom(uniqueId, artifactType).left().map(derivedFrom -> fillProperties(uniqueId, artifactType, derivedFrom)).left()
+                .map(props -> artifactType);
+    }
+
+    private Optional<String> getAssociatedModelName(String uniqueId) {
+        final Either<ImmutablePair<ModelData, GraphEdge>, JanusGraphOperationStatus> modelName = janusGraphGenericDao.getParentNode(
+                UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ArtifactType), uniqueId, GraphEdgeLabels.MODEL_ELEMENT,
+                NodeTypeEnum.Model, ModelData.class);
+        if (modelName.isRight()) {
+            return Optional.empty();
+        }
+        return Optional.ofNullable(modelName.left().value().getLeft().getName());
+    }
+
     /**
      * Adds a property definition to the given TOSCA artifact types definition
      * @param artifactType the TOSCA artifact types
@@ -285,6 +389,12 @@ public class ArtifactTypeOperation implements IArtifactTypeOperation {
         return result.left().value();
     }
 
+    private Either<ArtifactTypeData, StorageOperationStatus> fillDerivedFrom(String uniqueId, ArtifactTypeDefinition artifactType) {
+        LOGGER.debug("#fillDerivedFrom - fetching artifact type {} derived node", artifactType.getType());
+        return derivedFromOperation.getDerivedFromChild(uniqueId, NodeTypeEnum.ArtifactType, ArtifactTypeData.class).right()
+                .bind(this::handleDerivedFromNotExist).left().map(derivedFrom -> setDerivedFrom(artifactType, derivedFrom));
+    }
+
     private Either<ArtifactTypeData, StorageOperationStatus> handleDerivedFromNotExist(final StorageOperationStatus storageOperationStatus) {
         if (storageOperationStatus == StorageOperationStatus.NOT_FOUND) {
             return Either.left(null);
@@ -316,6 +426,20 @@ public class ArtifactTypeOperation implements IArtifactTypeOperation {
         }
     }
 
+    private Either<List<PropertyDefinition>, StorageOperationStatus> fillProperties(String uniqueId, ArtifactTypeDefinition artifactType,
+                                                                                    ArtifactTypeData derivedFromNode) {
+        LOGGER.debug("#fillProperties - fetching all properties for artifact type {}", artifactType.getType());
+        return propertyOperation.findPropertiesOfNode(NodeTypeEnum.ArtifactType, uniqueId).right().bind(this::handleArtifactTypeHasNoProperties).left()
+                .bind(propsMap -> fillDerivedFromProperties(artifactType, derivedFromNode, new ArrayList<>(propsMap.values())));
+    }
+
+    private Either<Map<String, PropertyDefinition>, StorageOperationStatus> handleArtifactTypeHasNoProperties(JanusGraphOperationStatus err) {
+        if (err == JanusGraphOperationStatus.NOT_FOUND) {
+            return Either.left(new HashMap<>());
+        }
+        return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(err));
+    }
+
     private Either<Map<String, PropertyDefinition>, StorageOperationStatus> handleNoProperties(
         final JanusGraphOperationStatus janusGraphOperationStatus) {
         if (janusGraphOperationStatus == JanusGraphOperationStatus.NOT_FOUND) {
diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ArtifactTypeOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ArtifactTypeOperationTest.java
new file mode 100644 (file)
index 0000000..4ec0963
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2022 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ *
+ *
+ */
+
+package org.openecomp.sdc.be.model.operations.impl;
+
+import fj.data.Either;
+import org.janusgraph.core.JanusGraph;
+import org.janusgraph.core.JanusGraphVertex;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.openecomp.sdc.be.dao.janusgraph.HealingJanusGraphGenericDao;
+import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao;
+import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
+import org.openecomp.sdc.be.datatypes.elements.ArtifactTypeDataDefinition;
+import org.openecomp.sdc.be.model.ArtifactTypeDefinition;
+import org.openecomp.sdc.be.model.ModelTestBase;
+import org.openecomp.sdc.be.model.PropertyDefinition;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
+
+import java.util.Iterator;
+
+import static java.util.Arrays.asList;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+
+@SpringJUnitConfig(locations = "classpath:application-context-test.xml")
+public class ArtifactTypeOperationTest extends ModelTestBase {
+
+    private static final String NULL_STRING = null;
+
+    @Autowired
+    private ArtifactTypeOperation artifactTypeOperation;
+
+    @Autowired
+    private HealingJanusGraphGenericDao janusGraphGenericDao;
+
+    @BeforeAll
+    public static void setupBeforeClass() {
+        ModelTestBase.init();
+    }
+
+    @BeforeEach
+    public void cleanUp() {
+        JanusGraphGenericDao janusGraphGenericDao = artifactTypeOperation.janusGraphGenericDao;
+        Either<JanusGraph, JanusGraphOperationStatus> graphResult = janusGraphGenericDao.getGraph();
+        JanusGraph graph = graphResult.left().value();
+
+        Iterable<JanusGraphVertex> vertices = graph.query().vertices();
+        if (vertices != null) {
+            Iterator<JanusGraphVertex> iterator = vertices.iterator();
+            while (iterator.hasNext()) {
+                JanusGraphVertex vertex = iterator.next();
+                vertex.remove();
+            }
+        }
+        janusGraphGenericDao.commit();
+    }
+
+    @Test
+    public void createArtifactType() {
+
+        ArtifactTypeDefinition createdType = createArtifactTypeDef("type1", "description1", "derivedFromTest1");
+        artifactTypeOperation.createArtifactType(createdType, false);
+
+        assertNotNull(createdType);
+        assertEquals("type1name", createdType.getName());
+        assertEquals("description1", createdType.getDescription());
+        assertEquals("derivedFromTest1", createdType.getDerivedFrom());
+    }
+
+    @Test
+    public void createAndModifyArtifactType() {
+
+        ArtifactTypeDefinition createdType = createArtifactTypeDef("type2", "description1", "derivedFromTest2");
+        artifactTypeOperation.createArtifactType(createdType, false);
+
+        createdType = createArtifactTypeDef("type2", "description2", NULL_STRING);
+        createdType.setName("newName2");
+
+        createdType = artifactTypeOperation.createArtifactType(createdType, false);
+        assertNotNull(createdType);
+        assertEquals("newName2", createdType.getName());
+        assertEquals("description2", createdType.getDescription());
+        assertEquals(null, createdType.getDerivedFrom());
+    }
+
+    @Test
+    public void createAndModifyArtifactType_WithProps() {
+
+        ArtifactTypeDefinition createdType = createArtifactTypeDef("type3", "description1", "derivedFromTest3");
+        artifactTypeOperation.createArtifactType(createdType, false);
+
+        PropertyDefinition prop1 = createSimpleProperty("val1", "prop1", "string");
+        PropertyDefinition prop2 = createSimpleProperty("val2", "prop2", "string");
+        createdType = createArtifactTypeDef("type3", "description2", NULL_STRING, prop1, prop2);
+        createdType.setName("newName");
+
+        createdType = artifactTypeOperation.createArtifactType(createdType, false);
+        assertNotNull(createdType);
+        assertEquals("newName", createdType.getName());
+        assertEquals("description2", createdType.getDescription());
+        assertEquals(null, createdType.getDerivedFrom());
+        assertEquals(2, createdType.getProperties().size());
+    }
+
+    private ArtifactTypeDefinition createArtifactTypeDef(String type, String description, String derivedFrom) {
+        return createArtifactTypeDef(type, description, derivedFrom, null);
+    }
+
+    private ArtifactTypeDefinition createArtifactTypeDef(String type, String description, String derivedFrom, PropertyDefinition... props) {
+        ArtifactTypeDataDefinition artifactTypeDataDefinition = new ArtifactTypeDataDefinition();
+        artifactTypeDataDefinition.setDescription(description);
+        artifactTypeDataDefinition.setType(type);
+        artifactTypeDataDefinition.setName(type + "name");
+        artifactTypeDataDefinition.setDerivedFrom(derivedFrom);
+        ArtifactTypeDefinition artifactTypeDefinition = new ArtifactTypeDefinition(artifactTypeDataDefinition);
+
+        if (props != null) {
+            artifactTypeDefinition.setProperties(asList(props));
+        }
+        return artifactTypeDefinition;
+    }
+
+}
\ No newline at end of file