From a5362c70a2dc989a5cbc7ac2e4129b12b8bde53f Mon Sep 17 00:00:00 2001 From: franciscovila Date: Mon, 17 Jan 2022 10:41:23 +0000 Subject: [PATCH] Add support for update to artifact types endpoint Creates a new artifact type or overwrites it, if this already exists. Issue-ID: SDC-3846 Signed-off-by: franciscovila Change-Id: Ia380dd4d790a94aa030ccf434de0440cc620f55c --- .../operations/impl/ArtifactTypeOperation.java | 178 +++++++++++++++++---- .../operations/impl/ArtifactTypeOperationTest.java | 144 +++++++++++++++++ 2 files changed, 295 insertions(+), 27 deletions(-) create mode 100644 catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ArtifactTypeOperationTest.java diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ArtifactTypeOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ArtifactTypeOperation.java index d8a78cdd7d..d16a7353cb 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ArtifactTypeOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ArtifactTypeOperation.java @@ -20,23 +20,16 @@ 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 createNodeResult = null; + Either createNodeResult = null; try { artifactType.setUniqueId(UniqueIdBuilder.buildArtifactTypeUid(artifactType.getModel(), artifactType.getType())); final ArtifactTypeData artifactTypeData = new ArtifactTypeData(artifactType); final Either 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, StorageOperationStatus> updateArtifactProperties(final String artifactId, final List 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 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, StorageOperationStatus> addPropertiesToArtifact(String artifactId, + List 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 getProjectionLatestArtifactTypeByType(String type, String model) { + Map 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 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 getArtifactTypeByCriteria(String type, Map properties, String model) { + Either result; + if (type == null || type.isEmpty()) { + LOGGER.error("type is empty"); + result = Either.right(StorageOperationStatus.INVALID_ID); + return result; + } + Either, 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 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 createArtifactTypeDefinition(String uniqueId, ArtifactTypeData artifactTypeNode) { + ArtifactTypeDefinition artifactType = new ArtifactTypeDefinition(artifactTypeNode.getArtifactTypeDataDefinition()); + Optional 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 getAssociatedModelName(String uniqueId) { + final Either, 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 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 handleDerivedFromNotExist(final StorageOperationStatus storageOperationStatus) { if (storageOperationStatus == StorageOperationStatus.NOT_FOUND) { return Either.left(null); @@ -316,6 +426,20 @@ public class ArtifactTypeOperation implements IArtifactTypeOperation { } } + private Either, 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, StorageOperationStatus> handleArtifactTypeHasNoProperties(JanusGraphOperationStatus err) { + if (err == JanusGraphOperationStatus.NOT_FOUND) { + return Either.left(new HashMap<>()); + } + return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(err)); + } + private Either, 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 index 0000000000..4ec0963fec --- /dev/null +++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ArtifactTypeOperationTest.java @@ -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 graphResult = janusGraphGenericDao.getGraph(); + JanusGraph graph = graphResult.left().value(); + + Iterable vertices = graph.query().vertices(); + if (vertices != null) { + Iterator 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 -- 2.16.6