From c82aebcde26e34c4151531b4d7a8f6e7689734ba Mon Sep 17 00:00:00 2001 From: "andre.schmid" Date: Fri, 14 May 2021 20:38:45 +0100 Subject: Add models imports endpoint and persistence structure MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Create the structure to persist the model imports. Changed create model API, allowing to create a model along its TOSCA descriptor import structure. Introduced an endpoint to update the imports of a model. Change-Id: Ic775ef544051c29c721cacc20b37c2fb20338be9 Issue-ID: SDC-3614 Signed-off-by: André Schmid --- .../exception/ModelOperationExceptionSupplier.java | 50 +++++++ .../be/model/operations/impl/ModelOperation.java | 93 ++++++++++-- .../model/operations/impl/ModelOperationTest.java | 160 ++++++++++++++++++++- 3 files changed, 287 insertions(+), 16 deletions(-) create mode 100644 catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/exception/ModelOperationExceptionSupplier.java (limited to 'catalog-model') diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/exception/ModelOperationExceptionSupplier.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/exception/ModelOperationExceptionSupplier.java new file mode 100644 index 0000000000..c2ad071d13 --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/exception/ModelOperationExceptionSupplier.java @@ -0,0 +1,50 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 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.jsonjanusgraph.operations.exception; + +import java.util.function.Supplier; +import org.openecomp.sdc.be.dao.api.ActionStatus; + +/** + * Supplies operation exception needed by the the Model logic + */ +public class ModelOperationExceptionSupplier { + + private ModelOperationExceptionSupplier() { + + } + + public static Supplier invalidModel(final String modelName) { + return () -> new OperationException(ActionStatus.INVALID_MODEL, modelName); + } + + public static Supplier emptyModelImports() { + return () -> new OperationException(ActionStatus.MODEL_IMPORTS_IS_EMPTY); + } + + public static Supplier couldNotReadImports() { + return () -> new OperationException(ActionStatus.COULD_NOT_READ_MODEL_IMPORTS); + } + + public static Supplier modelAlreadyExists(final String modelName) { + return () -> new OperationException(ActionStatus.MODEL_ALREADY_EXISTS, modelName); + } + +} diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java index c604df6dde..ccc18e57dd 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java @@ -19,12 +19,27 @@ package org.openecomp.sdc.be.model.operations.impl; import fj.data.Either; +import java.nio.charset.StandardCharsets; +import java.util.EnumMap; +import java.util.List; +import java.util.Map; import java.util.Objects; +import java.util.Optional; +import java.util.stream.Collectors; +import org.apache.commons.collections.MapUtils; +import org.apache.commons.lang3.StringUtils; import org.openecomp.sdc.be.dao.api.ActionStatus; +import org.openecomp.sdc.be.dao.cassandra.ToscaModelImportCassandraDao; 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.data.model.ToscaImportByModel; +import org.openecomp.sdc.be.datatypes.enums.GraphPropertyEnum; import org.openecomp.sdc.be.model.Model; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; +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; import org.openecomp.sdc.common.log.wrappers.Logger; @@ -32,28 +47,34 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component("model-operation") -public class ModelOperation extends AbstractOperation { +public class ModelOperation { private static final Logger log = Logger.getLogger(ModelOperation.class); - private final JanusGraphGenericDao genericDao; + private final JanusGraphGenericDao janusGraphGenericDao; + private final JanusGraphDao janusGraphDao; + private final ToscaModelImportCassandraDao toscaModelImportCassandraDao; @Autowired - public ModelOperation(final JanusGraphGenericDao janusGraphGenericDao) { - this.genericDao = janusGraphGenericDao; + public ModelOperation(final JanusGraphGenericDao janusGraphGenericDao, + final JanusGraphDao janusGraphDao, + final ToscaModelImportCassandraDao toscaModelImportCassandraDao) { + this.janusGraphGenericDao = janusGraphGenericDao; + this.janusGraphDao = janusGraphDao; + this.toscaModelImportCassandraDao = toscaModelImportCassandraDao; } public Model createModel(final Model model, final boolean inTransaction) { Model result = null; - final ModelData modelData = new ModelData(model.getName(), UniqueIdBuilder.buildModelUid(model.getName())); + final var modelData = new ModelData(model.getName(), UniqueIdBuilder.buildModelUid(model.getName())); try { - final Either createNode = genericDao.createNode(modelData, ModelData.class); + final Either createNode = janusGraphGenericDao.createNode(modelData, ModelData.class); if (createNode.isRight()) { - final JanusGraphOperationStatus janusGraphOperationStatus = createNode.right().value(); + final var janusGraphOperationStatus = createNode.right().value(); log.error(EcompLoggerErrorCode.DATA_ERROR, ModelOperation.class.getName(), "Problem while creating model, reason {}", janusGraphOperationStatus); if (janusGraphOperationStatus == JanusGraphOperationStatus.JANUSGRAPH_SCHEMA_VIOLATION) { - throw new OperationException(ActionStatus.MODEL_ALREADY_EXISTS, model.getName()); + throw ModelOperationExceptionSupplier.modelAlreadyExists(model.getName()).get(); } throw new OperationException(ActionStatus.GENERAL_ERROR, String.format("Failed to create model %s on JanusGraph with %s error", model, janusGraphOperationStatus)); @@ -63,14 +84,66 @@ public class ModelOperation extends AbstractOperation { } finally { if (!inTransaction) { if (Objects.nonNull(result)) { - genericDao.commit(); + janusGraphGenericDao.commit(); } else { - genericDao.rollback(); + janusGraphGenericDao.rollback(); } } } } + public Optional findModelVertexByName(final String name) { + if (StringUtils.isEmpty(name)) { + return Optional.empty(); + } + final Map props = new EnumMap<>(GraphPropertyEnum.class); + props.put(GraphPropertyEnum.NAME, name); + props.put(GraphPropertyEnum.UNIQUE_ID, UniqueIdBuilder.buildModelUid(name)); + final Either, JanusGraphOperationStatus> result = janusGraphDao.getByCriteria(VertexTypeEnum.MODEL, props); + if (result.isRight()) { + final JanusGraphOperationStatus janusGraphOperationStatus = result.right().value(); + if (janusGraphOperationStatus == JanusGraphOperationStatus.NOT_FOUND) { + return Optional.empty(); + } + log.error(EcompLoggerErrorCode.DATA_ERROR, this.getClass().getName(), + String.format("Problem while getting model %s. reason %s", name, janusGraphOperationStatus)); + throw new OperationException(ActionStatus.GENERAL_ERROR, + String.format("Failed to get model %s on JanusGraph with %s error", name, janusGraphOperationStatus)); + } + return Optional.ofNullable(result.left().value().get(0)); + } + + public Optional findModelByName(final String name) { + if (StringUtils.isEmpty(name)) { + return Optional.empty(); + } + final Optional modelVertexOpt = findModelVertexByName(name); + if (modelVertexOpt.isEmpty()) { + return Optional.empty(); + } + + final GraphVertex graphVertex = modelVertexOpt.get(); + final var model = new Model((String) graphVertex.getMetadataProperty(GraphPropertyEnum.NAME)); + return Optional.of(model); + } + + public void createModelImports(final String modelId, final Map zipContent) { + if (MapUtils.isEmpty(zipContent)) { + return; + } + final List toscaImportByModelList = zipContent.entrySet().stream() + .map(entry -> { + final String path = entry.getKey(); + final byte[] bytes = entry.getValue(); + final String content = new String(bytes, StandardCharsets.UTF_8); + final var toscaImportByModel = new ToscaImportByModel(); + toscaImportByModel.setModelId(modelId); + toscaImportByModel.setFullPath(path); + toscaImportByModel.setContent(content); + return toscaImportByModel; + }).collect(Collectors.toList()); + toscaModelImportCassandraDao.importAll(modelId, toscaImportByModelList); + } } diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java index c1c0132b2c..620c7f76a6 100644 --- a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java +++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java @@ -19,20 +19,40 @@ package org.openecomp.sdc.be.model.operations.impl; import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; 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.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import fj.data.Either; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.TreeMap; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.TestInstance; -import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.mockito.ArgumentCaptor; import org.mockito.InjectMocks; 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.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.data.model.ToscaImportByModel; +import org.openecomp.sdc.be.datatypes.enums.GraphPropertyEnum; import org.openecomp.sdc.be.model.Model; import org.openecomp.sdc.be.model.ModelTestBase; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; @@ -40,19 +60,26 @@ import org.openecomp.sdc.be.resources.data.ModelData; import org.springframework.test.context.ContextConfiguration; @ContextConfiguration("classpath:application-context-test.xml") -@TestInstance(Lifecycle.PER_CLASS) class ModelOperationTest extends ModelTestBase { @InjectMocks private ModelOperation modelOperation; @Mock private JanusGraphGenericDao janusGraphGenericDao; + @Mock + private JanusGraphDao janusGraphDao; + @Mock + private ToscaModelImportCassandraDao toscaModelImportCassandraDao; private final String modelName = "ETSI-SDC-MODEL-TEST"; @BeforeAll - void setup() { + static void beforeAllInit() { init(); + } + + @BeforeEach + void beforeEachInit() { MockitoAnnotations.openMocks(this); } @@ -68,13 +95,134 @@ class ModelOperationTest extends ModelTestBase { @Test void createModelFailWithModelAlreadyExistTest() { when(janusGraphGenericDao.createNode(any(),any())).thenReturn(Either.right(JanusGraphOperationStatus.JANUSGRAPH_SCHEMA_VIOLATION)); - assertThrows(OperationException.class, () -> modelOperation.createModel(new Model(modelName), false)); + final var model = new Model(modelName); + assertThrows(OperationException.class, () -> modelOperation.createModel(model, false)); } @Test void createModelFailTest() { when(janusGraphGenericDao.createNode(any(),any())).thenReturn(Either.right(JanusGraphOperationStatus.GRAPH_IS_NOT_AVAILABLE)); - assertThrows(OperationException.class, () -> modelOperation.createModel(new Model(modelName), false)); + final var model = new Model(modelName); + assertThrows(OperationException.class, () -> modelOperation.createModel(model, false)); + } + + @Test + void createModelImportsSuccessTest() { + var modelId = "modelId"; + var contentEntry1 = "contentEntry1"; + var pathEntry1 = "entry1"; + var contentEntry2 = "contentEntry2"; + var pathEntry2 = "entry2/path"; + final Map zipContent = new TreeMap<>(); + zipContent.put(pathEntry1, contentEntry1.getBytes(StandardCharsets.UTF_8)); + zipContent.put(pathEntry2, contentEntry2.getBytes(StandardCharsets.UTF_8)); + + modelOperation.createModelImports(modelId, zipContent); + + final var toscaImport1 = new ToscaImportByModel(); + toscaImport1.setModelId(modelId); + toscaImport1.setContent(contentEntry1); + toscaImport1.setFullPath(pathEntry1); + final var toscaImport2 = new ToscaImportByModel(); + toscaImport2.setModelId(modelId); + toscaImport2.setContent(contentEntry2); + toscaImport2.setFullPath(pathEntry2); + final List toscaImportByModelList = List.of(toscaImport1, toscaImport2); + + verify(toscaModelImportCassandraDao).importAll(modelId, toscaImportByModelList); + } + + @Test + void createModelImportsTest_emptyZipContent() { + var modelId = "modelId"; + modelOperation.createModelImports(modelId, Collections.emptyMap()); + verify(toscaModelImportCassandraDao, never()).importAll(eq(modelId), anyList()); + modelOperation.createModelImports(modelId, null); + verify(toscaModelImportCassandraDao, never()).importAll(eq(null), anyList()); + } + + @Test + void findModelVertexSuccessTest() { + final ArgumentCaptor> mapArgumentCaptor = ArgumentCaptor.forClass(Map.class); + final GraphVertex expectedVertex = new GraphVertex(); + when(janusGraphDao.getByCriteria(eq(VertexTypeEnum.MODEL), mapArgumentCaptor.capture())).thenReturn(Either.left(List.of(expectedVertex))); + var modelName = "modelName"; + final Optional modelVertexByNameOpt = modelOperation.findModelVertexByName(modelName); + assertTrue(modelVertexByNameOpt.isPresent()); + assertEquals(expectedVertex, modelVertexByNameOpt.get()); + final Map value = mapArgumentCaptor.getValue(); + assertEquals(modelName, value.get(GraphPropertyEnum.NAME)); + assertEquals(UniqueIdBuilder.buildModelUid(modelName), value.get(GraphPropertyEnum.UNIQUE_ID)); + } + + @Test + void findModelVertexTest_modelNotFound() { + final ArgumentCaptor> mapArgumentCaptor = ArgumentCaptor.forClass(Map.class); + when(janusGraphDao.getByCriteria(eq(VertexTypeEnum.MODEL), mapArgumentCaptor.capture())) + .thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND)); + var modelName = "modelName"; + + final Optional modelVertexByNameOpt = modelOperation.findModelVertexByName(modelName); + + assertTrue(modelVertexByNameOpt.isEmpty()); + final Map value = mapArgumentCaptor.getValue(); + assertEquals(modelName, value.get(GraphPropertyEnum.NAME)); + assertEquals(UniqueIdBuilder.buildModelUid(modelName), value.get(GraphPropertyEnum.UNIQUE_ID)); + } + + @Test + void findModelVertexTest_janusGraphError() { + final ArgumentCaptor> mapArgumentCaptor = ArgumentCaptor.forClass(Map.class); + when(janusGraphDao.getByCriteria(eq(VertexTypeEnum.MODEL), mapArgumentCaptor.capture())) + .thenReturn(Either.right(JanusGraphOperationStatus.GENERAL_ERROR)); + var modelName = "modelName"; + + final var actualException = assertThrows(OperationException.class, () -> modelOperation.findModelVertexByName(modelName)); + + assertEquals(ActionStatus.GENERAL_ERROR, actualException.getActionStatus()); + final Map value = mapArgumentCaptor.getValue(); + assertEquals(modelName, value.get(GraphPropertyEnum.NAME)); + assertEquals(UniqueIdBuilder.buildModelUid(modelName), value.get(GraphPropertyEnum.UNIQUE_ID)); + } + + @Test + void findModelVertexTest_emptyOrNullModelName() { + assertTrue(modelOperation.findModelVertexByName("").isEmpty()); + assertTrue(modelOperation.findModelVertexByName(null).isEmpty()); + } + + @Test + void findModelByNameSuccessTest() { + final ArgumentCaptor> mapArgumentCaptor = ArgumentCaptor.forClass(Map.class); + var modelName = "modelName"; + 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))); + final Optional modelByNameOpt = modelOperation.findModelByName(modelName); + + final Map value = mapArgumentCaptor.getValue(); + assertEquals(modelName, value.get(GraphPropertyEnum.NAME)); + assertEquals(UniqueIdBuilder.buildModelUid(modelName), value.get(GraphPropertyEnum.UNIQUE_ID)); + + final Model expectedModel = new Model(modelName); + assertTrue(modelByNameOpt.isPresent()); + assertEquals(expectedModel, modelByNameOpt.get()); + } + + @Test + void findModelByNameTest_modelNameNotFound() { + final ArgumentCaptor> mapArgumentCaptor = ArgumentCaptor.forClass(Map.class); + var modelName = "modelName"; + when(janusGraphDao.getByCriteria(eq(VertexTypeEnum.MODEL), mapArgumentCaptor.capture())) + .thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND)); + final Optional modelByNameOpt = modelOperation.findModelByName(modelName); + assertTrue(modelByNameOpt.isEmpty()); + } + + @Test + void findModelByNameTest_emptyOrNullModelName() { + assertTrue(modelOperation.findModelByName("").isEmpty()); + assertTrue(modelOperation.findModelByName(null).isEmpty()); } } -- cgit 1.2.3-korg