Support models extending models 55/123255/3
authorMichaelMorris <michael.morris@est.tech>
Thu, 12 Aug 2021 08:14:21 +0000 (09:14 +0100)
committerAnderson Ribeiro <anderson.ribeiro@est.tech>
Fri, 13 Aug 2021 18:53:24 +0000 (18:53 +0000)
Signed-off-by: MichaelMorris <michael.morris@est.tech>
Issue-ID: SDC-3668
Change-Id: Iad4d2a28c1c982e55e8835d4f30a9a212aefb6be

catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ArchiveEndpointTest.java
catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ModelServletTest.java
catalog-dao/src/main/java/org/openecomp/sdc/be/dao/janusgraph/JanusGraphGenericDao.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/Model.java
catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java
catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/ModelCreateRequest.java
catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java

index faede3d..8a15f47 100644 (file)
@@ -264,7 +264,7 @@ class ArchiveEndpointTest extends JerseyTest {
 
         @Bean
         ModelOperation modelOperation() {
-            return new ModelOperation(null, null, null);
+            return new ModelOperation(null, null, null, null);
         }
 
         private void initGraphForTest() {
index e40124f..4e1c0e7 100644 (file)
@@ -199,6 +199,21 @@ class ModelServletTest extends JerseyTest {
             .post(Entity.entity(formDataMultiPart, MediaType.MULTIPART_FORM_DATA));
         assertEquals(Status.OK.getStatusCode(), response.getStatus());
     }
+    
+    @Test
+    void createModelWithDerivedFromSuccessTest() throws JsonProcessingException {
+        when(responseFormat.getStatus()).thenReturn(HttpStatus.OK_200);
+        when(componentsUtils.getResponseFormat(ActionStatus.CREATED)).thenReturn(responseFormat);
+        when(modelBusinessLogic.createModel(any(Model.class))).thenReturn(model);
+        ModelCreateRequest derviedModelCreateRequest = new ModelCreateRequest();
+        derviedModelCreateRequest.setName("derivedModel");
+        derviedModelCreateRequest.setDerivedFrom(model.getName());
+        final FormDataMultiPart formDataMultiPart = buildCreateFormDataMultiPart(new byte[0], parseToJsonString(derviedModelCreateRequest));
+        final var response = target(rootPath.toString()).request(MediaType.APPLICATION_JSON)
+            .header(Constants.USER_ID_HEADER, USER_ID)
+            .post(Entity.entity(formDataMultiPart, MediaType.MULTIPART_FORM_DATA));
+        assertEquals(Status.OK.getStatusCode(), response.getStatus());
+    }
 
     @Test
     void createModelFailTest() throws JsonProcessingException {
index b2492cd..2116dcc 100644 (file)
@@ -693,7 +693,23 @@ public class JanusGraphGenericDao {
 
         if (modelVertices.isLeft()) {
             for (ImmutablePair<JanusGraphVertex, Edge> vertexPair : modelVertices.left().value()) {
-                if (model.equals((String)vertexPair.getLeft().property("name").value())) {
+                if (modelVertexMatchesModel(vertexPair.getLeft(), model)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+    
+    private boolean modelVertexMatchesModel(final JanusGraphVertex modelVertex, final String model) {
+        if (model.equals((String)modelVertex.property("name").value())) {
+            return true;
+        }
+        final Either<List<ImmutablePair<JanusGraphVertex, Edge>>, JanusGraphOperationStatus> derivedModels =
+                        getParentVerticies(modelVertex, GraphEdgeLabels.DERIVED_FROM);
+        if (derivedModels.isLeft()) {
+            for (final ImmutablePair<JanusGraphVertex, Edge> derivedModel : derivedModels.left().value()) {
+                if (modelVertexMatchesModel(derivedModel.left, model)) {
                     return true;
                 }
             }
index 99d0f65..9c07c05 100644 (file)
@@ -30,5 +30,10 @@ import lombok.NoArgsConstructor;
 public class Model {
 
     private String name;
+    private String derivedFrom;
+    
+    public Model(final String name) {
+       this.name = name;
+    }
 
 }
index ddc0367..d4bd799 100644 (file)
@@ -19,6 +19,7 @@
 package org.openecomp.sdc.be.model.operations.impl;
 
 import fj.data.Either;
+
 import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 import java.util.EnumMap;
@@ -29,17 +30,24 @@ import java.util.Optional;
 import java.util.stream.Collectors;
 import org.apache.commons.collections.MapUtils;
 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.cassandra.ToscaModelImportCassandraDao;
+import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
+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.jsongraph.GraphVertex;
 import org.openecomp.sdc.be.dao.jsongraph.JanusGraphDao;
 import org.openecomp.sdc.be.dao.jsongraph.types.VertexTypeEnum;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
 import org.openecomp.sdc.be.data.model.ToscaImportByModel;
 import org.openecomp.sdc.be.datatypes.enums.GraphPropertyEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
 import org.openecomp.sdc.be.model.Model;
 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException;
+import org.openecomp.sdc.be.model.operations.api.DerivedFromOperation;
+import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.ModelOperationExceptionSupplier;
 import org.openecomp.sdc.be.resources.data.ModelData;
 import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
@@ -55,14 +63,17 @@ public class ModelOperation {
     private final JanusGraphGenericDao janusGraphGenericDao;
     private final JanusGraphDao janusGraphDao;
     private final ToscaModelImportCassandraDao toscaModelImportCassandraDao;
+    private final DerivedFromOperation derivedFromOperation;
 
     @Autowired
     public ModelOperation(final JanusGraphGenericDao janusGraphGenericDao,
                           final JanusGraphDao janusGraphDao,
-                          final ToscaModelImportCassandraDao toscaModelImportCassandraDao) {
+                          final ToscaModelImportCassandraDao toscaModelImportCassandraDao,
+                          final DerivedFromOperation derivedFromOperation) {
         this.janusGraphGenericDao = janusGraphGenericDao;
         this.janusGraphDao = janusGraphDao;
         this.toscaModelImportCassandraDao = toscaModelImportCassandraDao;
+        this.derivedFromOperation = derivedFromOperation;
     }
 
     public Model createModel(final Model model, final boolean inTransaction) {
@@ -80,7 +91,8 @@ public class ModelOperation {
                 throw new OperationException(ActionStatus.GENERAL_ERROR,
                     String.format("Failed to create model %s on JanusGraph with %s error", model, janusGraphOperationStatus));
             }
-            result = new Model(createNode.left().value().getName());
+            addDerivedFromRelation(model);
+            result = new Model(createNode.left().value().getName(), model.getDerivedFrom());
             return result;
         } finally {
             if (!inTransaction) {
@@ -92,6 +104,24 @@ public class ModelOperation {
             }
         }
     }
+    
+    private void addDerivedFromRelation(final Model model) {
+        final String derivedFrom = model.getDerivedFrom();
+        if (derivedFrom == null) {
+            return;
+        }
+        log.debug("Adding derived from relation between model {} to its parent {}",
+                model.getName(), derivedFrom);
+        final Optional<Model> derivedFromModelOptional = this.findModelByName(derivedFrom);
+        if (derivedFromModelOptional.isPresent()) {
+            final Either<GraphRelation, StorageOperationStatus> result = derivedFromOperation.addDerivedFromRelation(UniqueIdBuilder.buildModelUid(model.getName()), 
+                    UniqueIdBuilder.buildModelUid(derivedFromModelOptional.get().getName()), NodeTypeEnum.Model);
+            if(result.isRight()) {
+                throw new OperationException(ActionStatus.GENERAL_ERROR,
+                    String.format("Failed to create relationship from model % to derived from model %s on JanusGraph with %s error", model, derivedFrom, result.right().value()));
+            }
+        }
+    }
 
     public Optional<GraphVertex> findModelVertexByName(final String name) {
         if (StringUtils.isEmpty(name)) {
@@ -171,7 +201,24 @@ public class ModelOperation {
     }
 
     private Model convertToModel(final GraphVertex modelGraphVertex) {
-        return new Model((String) modelGraphVertex.getMetadataProperty(GraphPropertyEnum.NAME));
+        final String modelName = (String) modelGraphVertex.getMetadataProperty(GraphPropertyEnum.NAME);
+
+        final Either<ImmutablePair<ModelData, GraphEdge>, JanusGraphOperationStatus> parentNode =
+                        janusGraphGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Model), UniqueIdBuilder.buildModelUid(modelName),
+                                        GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Model, ModelData.class);
+        log.debug("After retrieving DERIVED_FROM node of {}. status is {}", modelName, parentNode);
+        if (parentNode.isRight()) {
+            final JanusGraphOperationStatus janusGraphOperationStatus = parentNode.right().value();
+            if (janusGraphOperationStatus != JanusGraphOperationStatus.NOT_FOUND) {
+                final var operationException = ModelOperationExceptionSupplier.failedToRetrieveModels(janusGraphOperationStatus).get();
+                log.error(EcompLoggerErrorCode.DATA_ERROR, this.getClass().getName(), operationException.getMessage());
+                throw operationException;
+            }
+            return new Model((String) modelGraphVertex.getMetadataProperty(GraphPropertyEnum.NAME));
+        } else {
+            final ModelData parentModel = parentNode.left().value().getKey();
+            return new Model((String) modelGraphVertex.getMetadataProperty(GraphPropertyEnum.NAME), parentModel.getName());
+        }
     }
 }
 
index 0d294b4..685d95e 100644 (file)
@@ -20,17 +20,20 @@ package org.openecomp.sdc.be.ui.model;
 
 import javax.validation.constraints.NotNull;
 import javax.validation.constraints.Size;
+import lombok.AllArgsConstructor;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 
 /**
  * This class is responsible for holding all required fields from the create Model post request.
  * It also validates the Model 'name' field.
  */
-@Data
+@Data @AllArgsConstructor @NoArgsConstructor
 public class ModelCreateRequest {
 
     @NotNull(message = "Model name cannot be null")
     @Size(min = 2, message = "Model name cannot be empty")
     private String name;
 
+    private String derivedFrom;
 }
index c48a152..e8d01db 100644 (file)
@@ -25,6 +25,8 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.anyList;
+import static org.mockito.ArgumentMatchers.anyMap;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -47,17 +49,21 @@ import org.mockito.Mock;
 import org.mockito.MockitoAnnotations;
 import org.openecomp.sdc.be.dao.api.ActionStatus;
 import org.openecomp.sdc.be.dao.cassandra.ToscaModelImportCassandraDao;
+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.jsongraph.GraphVertex;
 import org.openecomp.sdc.be.dao.jsongraph.JanusGraphDao;
 import org.openecomp.sdc.be.dao.jsongraph.types.VertexTypeEnum;
+import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
 import org.openecomp.sdc.be.data.model.ToscaImportByModel;
 import org.openecomp.sdc.be.datatypes.enums.GraphPropertyEnum;
+import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
 import org.openecomp.sdc.be.model.Model;
 import org.openecomp.sdc.be.model.ModelTestBase;
 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.ModelOperationExceptionSupplier;
 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException;
+import org.openecomp.sdc.be.model.operations.api.DerivedFromOperation;
 import org.openecomp.sdc.be.resources.data.ModelData;
 import org.springframework.test.context.ContextConfiguration;
 
@@ -72,6 +78,8 @@ class ModelOperationTest extends ModelTestBase {
     private JanusGraphDao janusGraphDao;
     @Mock
     private ToscaModelImportCassandraDao toscaModelImportCassandraDao;
+    @Mock
+    private DerivedFromOperation derivedFromOperation;
 
     private final String modelName = "ETSI-SDC-MODEL-TEST";
 
@@ -93,6 +101,23 @@ class ModelOperationTest extends ModelTestBase {
         assertThat(createdModel).isNotNull();
         assertThat(createdModel.getName()).isEqualTo(modelName);
     }
+    
+    @Test
+    void createDerivedModelSuccessTest() {
+        final String derivedModelName = "derivedModel";
+        final ModelData modelData = new ModelData(derivedModelName,  UniqueIdBuilder.buildModelUid(derivedModelName));
+        when(janusGraphGenericDao.createNode(any(),any())).thenReturn(Either.left(modelData));
+        
+        final GraphVertex modelVertex = new GraphVertex();
+        modelVertex.addMetadataProperty(GraphPropertyEnum.NAME, "baseModel");
+        when(janusGraphDao.getByCriteria(eq(VertexTypeEnum.MODEL), anyMap())).thenReturn(Either.left(Collections.singletonList(modelVertex)));
+        when(janusGraphGenericDao.getChild(eq("uid"), anyString(), eq(GraphEdgeLabels.DERIVED_FROM), eq(NodeTypeEnum.Model), eq(ModelData.class))).thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
+        when(derivedFromOperation.addDerivedFromRelation("model.derivedModel", "model.baseModel", NodeTypeEnum.Model)).thenReturn(Either.left(new GraphRelation()));
+        
+        final Model createdModel = modelOperation.createModel(new Model(derivedModelName, modelName), false);
+        assertThat(createdModel).isNotNull();
+        assertThat(createdModel.getName()).isEqualTo(derivedModelName);
+    }
 
     @Test
     void createModelFailWithModelAlreadyExistTest() {
@@ -200,6 +225,8 @@ class ModelOperationTest extends ModelTestBase {
         final GraphVertex expectedVertex = mock(GraphVertex.class);
         when(expectedVertex.getMetadataProperty(GraphPropertyEnum.NAME)).thenReturn(modelName);
         when(janusGraphDao.getByCriteria(eq(VertexTypeEnum.MODEL), mapArgumentCaptor.capture())).thenReturn(Either.left(List.of(expectedVertex)));
+        when(janusGraphGenericDao.getChild("uid", UniqueIdBuilder.buildModelUid(modelName), GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Model,
+            ModelData.class)).thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
         final Optional<Model> modelByNameOpt = modelOperation.findModelByName(modelName);
 
         final Map<GraphPropertyEnum, Object> value = mapArgumentCaptor.getValue();
@@ -232,6 +259,9 @@ class ModelOperationTest extends ModelTestBase {
         final GraphVertex expectedVertex = mock(GraphVertex.class);
         when(expectedVertex.getMetadataProperty(GraphPropertyEnum.NAME)).thenReturn(modelName);
         when(janusGraphDao.getByCriteria(VertexTypeEnum.MODEL, Collections.emptyMap())).thenReturn(Either.left(List.of(expectedVertex)));
+        when(janusGraphGenericDao.getChild("uid", UniqueIdBuilder.buildModelUid(modelName), GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Model,
+            ModelData.class)).thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
+
         final List<Model> actualModelList = modelOperation.findAllModels();
         assertFalse(actualModelList.isEmpty());
         assertEquals(1, actualModelList.size());