From b835031b83230c36649c6e77787867a465e0ac47 Mon Sep 17 00:00:00 2001 From: "andre.schmid" Date: Mon, 21 Jun 2021 22:25:28 +0100 Subject: Create REST endpoint to retrieve models MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ifca0095d84d5da4ab4b055942d893e9c6a259eb7 Issue-ID: SDC-3622 Signed-off-by: André Schmid --- .../sdc/be/components/impl/ModelBusinessLogic.java | 10 +++ .../openecomp/sdc/be/servlets/ModelServlet.java | 28 +++++++++ .../be/components/impl/ModelBusinessLogicTest.java | 17 ++++++ .../sdc/be/servlets/ModelServletTest.java | 71 ++++++++++++++++------ .../exception/ModelOperationExceptionSupplier.java | 6 ++ .../be/model/operations/impl/ModelOperation.java | 55 +++++++++++++---- .../model/operations/impl/ModelOperationTest.java | 36 +++++++++++ 7 files changed, 193 insertions(+), 30 deletions(-) diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogic.java index 7f68a00a8b..a048af4ac8 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogic.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogic.java @@ -21,6 +21,7 @@ package org.openecomp.sdc.be.components.impl; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.util.List; import java.util.Map; import java.util.Optional; import org.apache.commons.lang3.StringUtils; @@ -56,6 +57,15 @@ public class ModelBusinessLogic { return modelOperation.findModelByName(modelName); } + /** + * Loads the list of models. + * + * @return the list of models + */ + public List listModels() { + return modelOperation.findAllModels(); + } + public void createModelImports(final String modelName, final InputStream modelImportsZip) { if (StringUtils.isEmpty(modelName)) { throw ModelOperationExceptionSupplier.invalidModel(modelName).get(); diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ModelServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ModelServlet.java index 0c5e4aebd6..337c641b33 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ModelServlet.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ModelServlet.java @@ -30,10 +30,12 @@ import io.swagger.v3.oas.annotations.servers.Server; import io.swagger.v3.oas.annotations.tags.Tag; import java.io.InputStream; import java.util.Arrays; +import java.util.List; import javax.inject.Inject; import javax.validation.Valid; import javax.validation.constraints.NotNull; import javax.ws.rs.Consumes; +import javax.ws.rs.GET; import javax.ws.rs.HeaderParam; import javax.ws.rs.POST; import javax.ws.rs.PUT; @@ -121,6 +123,32 @@ public class ModelServlet extends AbstractValidationsServlet { } } + @GET + @Path("/model") + @Produces(MediaType.APPLICATION_JSON) + @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) + @Operation(method = "GET", summary = "List TOSCA models", description = "List all the existing TOSCA models", + responses = { + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Model.class)))), + @ApiResponse(responseCode = "200", description = "Listing successful"), + @ApiResponse(responseCode = "403", description = "Restricted operation") + } + ) + public Response listModels(@HeaderParam(value = Constants.USER_ID_HEADER) final String userId) { + validateUser(ValidationUtils.sanitizeInputString(userId)); + try { + final List modelList = modelBusinessLogic.listModels(); + return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.OK), RepresentationUtils.toRepresentation(modelList)); + } catch (final BusinessException e) { + throw e; + } catch (final Exception e) { + var errorMsg = "Unexpected error while listing the models"; + BeEcompErrorManager.getInstance().logBeRestApiGeneralError(errorMsg); + log.error(errorMsg, e); + return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)); + } + } + @PUT @Path("/model/imports") @Consumes(MediaType.MULTIPART_FORM_DATA) diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogicTest.java index b88bdfb01c..ef334f5510 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogicTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogicTest.java @@ -33,6 +33,8 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; +import java.util.Collections; +import java.util.List; import java.util.Map; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; @@ -190,4 +192,19 @@ class ModelBusinessLogicTest { actualModel = modelBusinessLogic.findModel(null); assertTrue(actualModel.isEmpty()); } + + @Test + void listModelsSuccessTest() { + final List expectedModelList = List.of(new Model()); + when(modelOperation.findAllModels()).thenReturn(expectedModelList); + final List actualModelList = modelBusinessLogic.listModels(); + assertEquals(expectedModelList, actualModelList, "The model list should be as expected"); + } + + @Test + void listModelsTest_emptyList() { + when(modelOperation.findAllModels()).thenReturn(Collections.emptyList()); + final List actualModelList = modelBusinessLogic.listModels(); + assertTrue(actualModelList.isEmpty(), "The model list should be empty"); + } } \ No newline at end of file diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ModelServletTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ModelServletTest.java index 5992be4e9d..12f803fd19 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ModelServletTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ModelServletTest.java @@ -26,12 +26,15 @@ import static org.mockito.Mockito.when; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; +import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; +import java.util.List; import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; import javax.ws.rs.client.Entity; +import javax.ws.rs.core.HttpHeaders; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response.Status; import org.apache.commons.lang3.StringUtils; @@ -44,11 +47,8 @@ import org.glassfish.jersey.server.ResourceConfig; import org.glassfish.jersey.test.JerseyTest; import org.glassfish.jersey.test.TestProperties; import org.junit.jupiter.api.AfterEach; -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.InjectMocks; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -79,7 +79,6 @@ import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.web.context.WebApplicationContext; -@TestInstance(Lifecycle.PER_CLASS) class ModelServletTest extends JerseyTest { private static final String USER_ID = "cs0008"; @@ -121,8 +120,20 @@ class ModelServletTest extends JerseyTest { private final Path rootPath = Path.of("/v1/catalog/model"); private final Path importsPath = rootPath.resolve("imports"); - @BeforeAll - public void initClass() { + @BeforeEach + void resetMock() throws Exception { + super.setUp(); + initMocks(); + initConfig(); + initTestData(); + } + + @AfterEach + void after() throws Exception { + super.tearDown(); + } + + private void initMocks() { when(request.getSession()).thenReturn(session); when(session.getServletContext()).thenReturn(servletContext); when(servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR)) @@ -132,6 +143,9 @@ class ModelServletTest extends JerseyTest { when(request.getHeader(Constants.USER_ID_HEADER)).thenReturn(USER_ID); when(webApplicationContext.getBean(ServletUtils.class)).thenReturn(servletUtils); when(servletUtils.getComponentsUtils()).thenReturn(componentsUtils); + } + + private void initConfig() { final String appConfigDir = "src/test/resources/config/catalog-be"; final ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir); final ConfigurationManager configurationManager = new ConfigurationManager(configurationSource); @@ -141,17 +155,6 @@ class ModelServletTest extends JerseyTest { ExternalConfiguration.setAppName("catalog-be"); } - @BeforeEach - void resetMock() throws Exception { - super.setUp(); - initTestData(); - } - - @AfterEach - void after() throws Exception { - super.tearDown(); - } - private void initTestData() { final String modelName = "MY-INTEGRATION-TEST-MODEL"; model = new Model(modelName); @@ -287,6 +290,40 @@ class ModelServletTest extends JerseyTest { assertEquals(Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus()); } + @Test + void listModelSuccessTest() throws IOException { + var model1 = new Model("model1"); + var model2 = new Model("model2"); + var model3 = new Model("model3"); + final List modelList = List.of(model1, model2, model3); + when(responseFormat.getStatus()).thenReturn(HttpStatus.OK_200); + when(componentsUtils.getResponseFormat(ActionStatus.OK)).thenReturn(responseFormat); + when(modelBusinessLogic.listModels()).thenReturn(modelList); + + final var response = target(rootPath.toString()).request(MediaType.APPLICATION_JSON) + .header(Constants.USER_ID_HEADER, USER_ID) + .get(); + + assertEquals(Status.OK.getStatusCode(), response.getStatus()); + assertEquals(MediaType.APPLICATION_JSON, response.getHeaderString(HttpHeaders.CONTENT_TYPE)); + final String responseBody = response.readEntity(String.class); + final String toRepresentation = (String) RepresentationUtils.toRepresentation(modelList); + assertEquals(toRepresentation, responseBody); + } + + @Test + void listModelErrorTest() { + when(responseFormat.getStatus()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR_500); + when(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR)).thenReturn(responseFormat); + doThrow(new RuntimeException()).when(modelBusinessLogic).listModels(); + + final var response = target(rootPath.toString()).request(MediaType.APPLICATION_JSON) + .header(Constants.USER_ID_HEADER, USER_ID) + .get(); + + assertEquals(Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus()); + } + private FormDataMultiPart buildUpdateFormDataMultiPart(final String modelName, final byte[] importFilesZip) { return new FormDataMultiPart() .field("modelName", modelName) 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 index c2ad071d13..6622bf5bd8 100644 --- 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 @@ -21,6 +21,7 @@ package org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception; import java.util.function.Supplier; import org.openecomp.sdc.be.dao.api.ActionStatus; +import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus; /** * Supplies operation exception needed by the the Model logic @@ -47,4 +48,9 @@ public class ModelOperationExceptionSupplier { return () -> new OperationException(ActionStatus.MODEL_ALREADY_EXISTS, modelName); } + public static Supplier failedToRetrieveModels(final JanusGraphOperationStatus janusGraphOperationStatus) { + var errorMsg = String.format("Failed to retrieve models. Status '%s'", janusGraphOperationStatus); + return () -> new OperationException(ActionStatus.GENERAL_ERROR, errorMsg); + } + } 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 ccc18e57dd..ddc0367a44 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 @@ -20,6 +20,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; import java.util.List; import java.util.Map; @@ -99,18 +100,11 @@ public class ModelOperation { 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)); + final List modelVerticesList = findModelVerticesByCriteria(props); + if (modelVerticesList.isEmpty()) { + return Optional.empty(); } - return Optional.ofNullable(result.left().value().get(0)); + return Optional.ofNullable(modelVerticesList.get(0)); } public Optional findModelByName(final String name) { @@ -123,8 +117,7 @@ public class ModelOperation { } final GraphVertex graphVertex = modelVertexOpt.get(); - final var model = new Model((String) graphVertex.getMetadataProperty(GraphPropertyEnum.NAME)); - return Optional.of(model); + return Optional.of(convertToModel(graphVertex)); } public void createModelImports(final String modelId, final Map zipContent) { @@ -144,6 +137,42 @@ public class ModelOperation { }).collect(Collectors.toList()); toscaModelImportCassandraDao.importAll(modelId, toscaImportByModelList); } + + /** + * Finds all the models. + * + * @return the list of models + */ + public List findAllModels() { + return findModelsByCriteria(Collections.emptyMap()); + } + + private List findModelsByCriteria(final Map propertyCriteria) { + final List modelVerticesByCriteria = findModelVerticesByCriteria(propertyCriteria); + if (modelVerticesByCriteria.isEmpty()) { + return Collections.emptyList(); + } + + return modelVerticesByCriteria.stream().map(this::convertToModel).collect(Collectors.toList()); + } + + private List findModelVerticesByCriteria(final Map propertyCriteria) { + final Either, JanusGraphOperationStatus> result = janusGraphDao.getByCriteria(VertexTypeEnum.MODEL, propertyCriteria); + if (result.isRight()) { + final var janusGraphOperationStatus = result.right().value(); + if (janusGraphOperationStatus == JanusGraphOperationStatus.NOT_FOUND) { + return Collections.emptyList(); + } + final var operationException = ModelOperationExceptionSupplier.failedToRetrieveModels(janusGraphOperationStatus).get(); + log.error(EcompLoggerErrorCode.DATA_ERROR, this.getClass().getName(), operationException.getMessage()); + throw operationException; + } + return result.left().value(); + } + + private Model convertToModel(final GraphVertex modelGraphVertex) { + return new Model((String) modelGraphVertex.getMetadataProperty(GraphPropertyEnum.NAME)); + } } 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 620c7f76a6..c48a1524b8 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 @@ -20,6 +20,7 @@ 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.assertFalse; import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -55,6 +56,7 @@ 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.ModelOperationExceptionSupplier; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; import org.openecomp.sdc.be.resources.data.ModelData; import org.springframework.test.context.ContextConfiguration; @@ -225,4 +227,38 @@ class ModelOperationTest extends ModelTestBase { assertTrue(modelOperation.findModelByName(null).isEmpty()); } + @Test + void findAllModelsSuccessTest() { + 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))); + final List actualModelList = modelOperation.findAllModels(); + assertFalse(actualModelList.isEmpty()); + assertEquals(1, actualModelList.size()); + assertEquals(modelName, actualModelList.get(0).getName()); + } + + @Test + void findAllModelsTest_noModelsFound() { + when(janusGraphDao.getByCriteria(VertexTypeEnum.MODEL, Collections.emptyMap())).thenReturn(Either.left(Collections.emptyList())); + final List actualModelList = modelOperation.findAllModels(); + assertTrue(actualModelList.isEmpty()); + } + + @Test + void findAllModelsTest_janusGraphNotFound() { + when(janusGraphDao.getByCriteria(VertexTypeEnum.MODEL, Collections.emptyMap())) + .thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND)); + final List actualModelList = modelOperation.findAllModels(); + assertTrue(actualModelList.isEmpty()); + } + + @Test + void findAllModelsTest_janusGraphError() { + when(janusGraphDao.getByCriteria(VertexTypeEnum.MODEL, Collections.emptyMap())) + .thenReturn(Either.right(JanusGraphOperationStatus.GENERAL_ERROR)); + final var actualException = assertThrows(OperationException.class, () -> modelOperation.findAllModels()); + final var expectedException = ModelOperationExceptionSupplier.failedToRetrieveModels(JanusGraphOperationStatus.GENERAL_ERROR).get(); + assertEquals(expectedException.getMessage(), actualException.getMessage()); + } } -- cgit 1.2.3-korg