aboutsummaryrefslogtreecommitdiffstats
path: root/catalog-be/src
diff options
context:
space:
mode:
authorandre.schmid <andre.schmid@est.tech>2021-05-14 20:38:45 +0100
committerChristophe Closset <christophe.closset@intl.att.com>2021-06-14 08:16:08 +0000
commitc82aebcde26e34c4151531b4d7a8f6e7689734ba (patch)
treefe14e6fadded7f43f9e1634b89d1fb9358b44253 /catalog-be/src
parentab6a90df6444ef7282fe9de8fe8107641bf7082f (diff)
Add models imports endpoint and persistence structure
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 <andre.schmid@est.tech>
Diffstat (limited to 'catalog-be/src')
-rw-r--r--catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml22
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogic.java63
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ModelServlet.java65
-rw-r--r--catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/OperationExceptionMapper.java11
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogicTest.java137
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/components/path/beans/ToscaModelImportCassandraDaoMock.java39
-rw-r--r--catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ModelServletTest.java138
-rw-r--r--catalog-be/src/test/resources/modelImports/emptyModelImports.zipbin0 -> 186 bytes
-rw-r--r--catalog-be/src/test/resources/modelImports/modelWithSubFolderAndEmptyFolder.zipbin0 -> 1961 bytes
-rw-r--r--catalog-be/src/test/resources/paths/path-context.xml1
10 files changed, 425 insertions, 51 deletions
diff --git a/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml b/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml
index 2c6a0c852b..b277aeef2f 100644
--- a/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml
+++ b/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml
@@ -2473,3 +2473,25 @@ errors:
message: "Error: Model name '%1' already exists.",
messageId: "SVC4144"
}
+
+ #---------SVC4145------------------------------
+ # %1 - "Model name"
+ INVALID_MODEL: {
+ code: 400,
+ message: "Invalid model '%1'.",
+ messageId: "SVC4145"
+ }
+
+ #---------SVC4146------------------------------
+ MODEL_IMPORTS_IS_EMPTY: {
+ code: 400,
+ message: "Given model imports zip is empty.",
+ messageId: "SVC4146"
+ }
+
+ #---------SVC4147------------------------------
+ COULD_NOT_READ_MODEL_IMPORTS: {
+ code: 400,
+ message: "Could not read imports zip.",
+ messageId: "SVC4147"
+ }
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 1ef4cef701..7f68a00a8b 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
@@ -18,17 +18,25 @@
*/
package org.openecomp.sdc.be.components.impl;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Map;
+import java.util.Optional;
+import org.apache.commons.lang3.StringUtils;
import org.openecomp.sdc.be.model.Model;
+import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.ModelOperationExceptionSupplier;
import org.openecomp.sdc.be.model.operations.impl.ModelOperation;
+import org.openecomp.sdc.common.zip.ZipUtils;
+import org.openecomp.sdc.common.zip.exception.ZipException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
-
@Component("modelBusinessLogic")
public class ModelBusinessLogic {
- private static final Logger log = LoggerFactory.getLogger(ModelBusinessLogic.class);
+ private static final Logger LOGGER = LoggerFactory.getLogger(ModelBusinessLogic.class);
private final ModelOperation modelOperation;
@Autowired
@@ -37,7 +45,56 @@ public class ModelBusinessLogic {
}
public Model createModel(final Model model) {
- log.debug("createModel: creating model {}", model);
+ LOGGER.debug("createModel: creating model {}", model);
return modelOperation.createModel(model, false);
}
+
+ public Optional<Model> findModel(final String modelName) {
+ if (StringUtils.isEmpty(modelName)) {
+ return Optional.empty();
+ }
+ return modelOperation.findModelByName(modelName);
+ }
+
+ public void createModelImports(final String modelName, final InputStream modelImportsZip) {
+ if (StringUtils.isEmpty(modelName)) {
+ throw ModelOperationExceptionSupplier.invalidModel(modelName).get();
+ }
+ if (modelImportsZip == null) {
+ throw ModelOperationExceptionSupplier.emptyModelImports().get();
+ }
+ if (findModel(modelName).isEmpty()) {
+ throw ModelOperationExceptionSupplier.invalidModel(modelName).get();
+ }
+
+ final var fileBytes = readBytes(modelImportsZip);
+ final Map<String, byte[]> zipFilesPathContentMap = unzipInMemory(fileBytes);
+ if (zipFilesPathContentMap.isEmpty()) {
+ throw ModelOperationExceptionSupplier.emptyModelImports().get();
+ }
+
+ modelOperation.createModelImports(modelName, zipFilesPathContentMap);
+ }
+
+ private Map<String, byte[]> unzipInMemory(final byte[] fileBytes) {
+ try {
+ return ZipUtils.readZip(fileBytes, false);
+ } catch (final ZipException e) {
+ throw ModelOperationExceptionSupplier.couldNotReadImports().get();
+ }
+ }
+
+ private byte[] readBytes(final InputStream modelImportsZip) {
+ try (final InputStream in = modelImportsZip; final ByteArrayOutputStream os = new ByteArrayOutputStream()) {
+ final var buffer = new byte[1024];
+ int len;
+ while ((len = in.read(buffer)) != -1) {
+ os.write(buffer, 0, len);
+ }
+ return os.toByteArray();
+ } catch (final IOException e) {
+ LOGGER.debug("Could not read the model imports zip", e);
+ throw ModelOperationExceptionSupplier.couldNotReadImports().get();
+ }
+ }
} \ No newline at end of file
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 f4fc883b4c..0c5e4aebd6 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
@@ -28,6 +28,7 @@ import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
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 javax.inject.Inject;
import javax.validation.Valid;
@@ -35,10 +36,13 @@ import javax.validation.constraints.NotNull;
import javax.ws.rs.Consumes;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.POST;
+import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
+import javax.ws.rs.core.Response.Status;
+import org.glassfish.jersey.media.multipart.FormDataParam;
import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
import org.openecomp.sdc.be.components.impl.ModelBusinessLogic;
import org.openecomp.sdc.be.components.impl.ResourceImportManager;
@@ -55,10 +59,10 @@ import org.openecomp.sdc.be.ui.model.ModelCreateRequest;
import org.openecomp.sdc.be.user.Role;
import org.openecomp.sdc.be.user.UserBusinessLogic;
import org.openecomp.sdc.common.api.Constants;
+import org.openecomp.sdc.common.util.ValidationUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
-import org.springframework.web.bind.annotation.RequestBody;
/**
* Root resource (exposed at "/" path)
@@ -85,34 +89,67 @@ public class ModelServlet extends AbstractValidationsServlet {
@POST
@Path("/model")
- @Consumes(MediaType.APPLICATION_JSON)
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
@Produces(MediaType.APPLICATION_JSON)
- @Operation(description = "Create model", method = "POST", summary = "Returns created model", responses = {
+ @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
+ @Operation(description = "Create a TOSCA model, along with its imports files", method = "POST", summary = "Create a TOSCA model", responses = {
@ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
@ApiResponse(responseCode = "201", description = "Model created"),
- @ApiResponse(responseCode = "403", description = "Restricted operation"),
@ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
- @ApiResponse(responseCode = "409", description = "Resource already exists")})
- @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
+ @ApiResponse(responseCode = "403", description = "Restricted operation"),
+ @ApiResponse(responseCode = "409", description = "Model already exists")})
public Response createModel(@Parameter(description = "model to be created", required = true)
- @Valid @RequestBody @NotNull final ModelCreateRequest modelCreateRequest,
- @HeaderParam(value = Constants.USER_ID_HEADER) String userId) {
-
- validateUser(userId);
+ @NotNull @Valid @FormDataParam("model") final ModelCreateRequest modelCreateRequest,
+ @Parameter(description = "the model TOSCA imports zipped", required = true)
+ @NotNull @FormDataParam("modelImportsZip") final InputStream modelImportsZip,
+ @HeaderParam(value = Constants.USER_ID_HEADER) final String userId) {
+ validateUser(ValidationUtils.sanitizeInputString(userId));
+ final var modelName = ValidationUtils.sanitizeInputString(modelCreateRequest.getName().trim());
try {
- final Model modelCreateResponse = modelBusinessLogic
+ final Model createdModel = modelBusinessLogic
.createModel(new JMapper<>(Model.class, ModelCreateRequest.class).getDestination(modelCreateRequest));
+ modelBusinessLogic.createModelImports(modelName, modelImportsZip);
return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED),
- RepresentationUtils.toRepresentation(modelCreateResponse));
+ RepresentationUtils.toRepresentation(createdModel));
+ } catch (final BusinessException e) {
+ throw e;
+ } catch (final Exception e) {
+ var errorMsg = String.format("Unexpected error while creating model '%s' imports", modelName);
+ BeEcompErrorManager.getInstance().logBeRestApiGeneralError(errorMsg);
+ log.error(errorMsg, e);
+ return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
+ }
+ }
+
+ @PUT
+ @Path("/model/imports")
+ @Consumes(MediaType.MULTIPART_FORM_DATA)
+ @Produces(MediaType.APPLICATION_JSON)
+ @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE)
+ @Operation(description = "Update a model TOSCA imports", method = "PUT", summary = "Update a model TOSCA imports", responses = {
+ @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))),
+ @ApiResponse(responseCode = "204", description = "Model imports updated"),
+ @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"),
+ @ApiResponse(responseCode = "403", description = "Restricted operation"),
+ @ApiResponse(responseCode = "404", description = "Model not found")})
+ public Response updateModelImports(@Parameter(description = "model to be created", required = true)
+ @NotNull @FormDataParam("modelName") String modelName,
+ @Parameter(description = "the model TOSCA imports zipped", required = true)
+ @NotNull @FormDataParam("modelImportsZip") final InputStream modelImportsZip,
+ @HeaderParam(value = Constants.USER_ID_HEADER) final String userId) {
+ validateUser(ValidationUtils.sanitizeInputString(userId));
+ modelName = ValidationUtils.sanitizeInputString(modelName);
+ try {
+ modelBusinessLogic.createModelImports(modelName, modelImportsZip);
} catch (final BusinessException e) {
throw e;
} catch (final Exception e) {
- var errorMsg = String
- .format("Unexpected error while creating model '%s'", modelCreateRequest.getName());
+ var errorMsg = String.format("Unexpected error while creating model '%s' imports", modelName);
BeEcompErrorManager.getInstance().logBeRestApiGeneralError(errorMsg);
log.error(errorMsg, e);
return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
}
+ return Response.status(Status.NO_CONTENT).build();
}
private void validateUser(final String userId) {
diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/OperationExceptionMapper.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/OperationExceptionMapper.java
index 7c25f8aef8..062e03b0da 100644
--- a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/OperationExceptionMapper.java
+++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/OperationExceptionMapper.java
@@ -26,6 +26,7 @@ import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException;
import org.openecomp.sdc.be.servlets.builder.ServletResponseBuilder;
import org.openecomp.sdc.common.log.wrappers.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@@ -35,16 +36,22 @@ public class OperationExceptionMapper implements ExceptionMapper<OperationExcept
private final ServletResponseBuilder servletResponseBuilder;
private final ResponseFormatManager responseFormatManager;
- private static final Logger log = Logger.getLogger(OperationExceptionMapper.class);
+ private static final Logger LOGGER = Logger.getLogger(OperationExceptionMapper.class);
+ @Autowired
public OperationExceptionMapper(final ServletResponseBuilder servletResponseBuilder) {
this.servletResponseBuilder = servletResponseBuilder;
this.responseFormatManager = ResponseFormatManager.getInstance();
}
+ public OperationExceptionMapper(final ServletResponseBuilder servletResponseBuilder, final ResponseFormatManager responseFormatManager) {
+ this.servletResponseBuilder = servletResponseBuilder;
+ this.responseFormatManager = responseFormatManager;
+ }
+
@Override
public Response toResponse(final OperationException exception) {
- log.debug("Handling OperationException response", exception);
+ LOGGER.debug("Handling OperationException response", exception);
return servletResponseBuilder.buildErrorResponse(responseFormatManager.getResponseFormat(exception.getActionStatus(), exception.getParams()));
}
}
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 6c7bd5323f..b88bdfb01c 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
@@ -19,16 +19,25 @@
package org.openecomp.sdc.be.components.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.anyMap;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.MethodOrderer.OrderAnnotation;
-import org.junit.jupiter.api.Order;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Map;
+import java.util.Optional;
+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.junit.jupiter.api.TestMethodOrder;
+import org.mockito.ArgumentCaptor;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@@ -36,10 +45,11 @@ import org.openecomp.sdc.be.dao.api.ActionStatus;
import org.openecomp.sdc.be.exception.BusinessException;
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.model.operations.impl.ModelOperation;
+import org.openecomp.sdc.common.zip.ZipUtils;
+import org.openecomp.sdc.common.zip.exception.ZipException;
-@TestInstance(Lifecycle.PER_CLASS)
-@TestMethodOrder(OrderAnnotation.class)
class ModelBusinessLogicTest {
@InjectMocks
@@ -47,8 +57,9 @@ class ModelBusinessLogicTest {
@Mock
private ModelOperation modelOperation;
private Model model;
+ private final Path modelImportsResourcePath = Path.of("src/test/resources/modelImports");
- @BeforeAll
+ @BeforeEach
void setup() {
MockitoAnnotations.openMocks(this);
initTestData();
@@ -59,7 +70,6 @@ class ModelBusinessLogicTest {
}
@Test
- @Order(1)
void createModelTest() {
when(modelOperation.createModel(model, false)).thenReturn(model);
final Model result = modelBusinessLogic.createModel(model);
@@ -68,13 +78,116 @@ class ModelBusinessLogicTest {
}
@Test
- @Order(2)
void createModelFailTest() {
when(modelOperation.createModel(model, false))
- .thenThrow(new OperationException(ActionStatus.MODEL_ALREADY_EXISTS, model.getName()));
+ .thenThrow(ModelOperationExceptionSupplier.modelAlreadyExists(model.getName()).get());
final BusinessException exception = assertThrows(BusinessException.class, () -> modelBusinessLogic.createModel(model));
assertThat(((OperationException) exception).getActionStatus().name()).isEqualTo(ActionStatus.MODEL_ALREADY_EXISTS.name());
assertThat(((OperationException) exception).getParams()).contains(model.getName());
}
+ @Test
+ void createModelImportsSuccessTest() throws IOException, ZipException {
+ final var modelId = "modelId";
+ final var resolve = modelImportsResourcePath.resolve("modelWithSubFolderAndEmptyFolder.zip");
+ final var zipBytes = Files.readAllBytes(resolve);
+ final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(zipBytes);
+ final Map<String, byte[]> expectedZipMap = ZipUtils.readZip(zipBytes, false);
+
+ when(modelOperation.findModelByName(modelId)).thenReturn(Optional.of(new Model(modelId)));
+ doNothing().when(modelOperation).createModelImports(eq(modelId), anyMap());
+
+ modelBusinessLogic.createModelImports(modelId, byteArrayInputStream);
+
+ final ArgumentCaptor<Map<String, byte[]>> zipMapArgumentCaptor = ArgumentCaptor.forClass(Map.class);
+ verify(modelOperation).createModelImports(eq(modelId), zipMapArgumentCaptor.capture());
+ expectedZipMap.keySet().forEach(key -> assertTrue(zipMapArgumentCaptor.getValue().containsKey(key), "Expecting import " + key));
+ }
+
+ @Test
+ void createModelImportsTest_invalidModel() {
+ //given an empty model
+ final var modelId = "";
+
+ final var emptyByteArrayInputStream = new ByteArrayInputStream(new byte[0]);
+ var actualOperationException = assertThrows(OperationException.class,
+ () -> modelBusinessLogic.createModelImports(modelId, emptyByteArrayInputStream));
+
+ var expectedOperationException = ModelOperationExceptionSupplier.invalidModel(modelId).get();
+ assertEquals(actualOperationException.getActionStatus(), expectedOperationException.getActionStatus());
+ assertEquals(actualOperationException.getParams().length, expectedOperationException.getParams().length);
+ assertEquals(actualOperationException.getParams()[0], expectedOperationException.getParams()[0]);
+
+ //given a null model
+ actualOperationException = assertThrows(OperationException.class,
+ () -> modelBusinessLogic.createModelImports(null, emptyByteArrayInputStream));
+
+ expectedOperationException = ModelOperationExceptionSupplier.invalidModel(null).get();
+ assertEquals(actualOperationException.getActionStatus(), expectedOperationException.getActionStatus());
+ assertEquals(actualOperationException.getParams().length, expectedOperationException.getParams().length);
+ assertEquals(actualOperationException.getParams()[0], expectedOperationException.getParams()[0]);
+ }
+
+ @Test
+ void createModelImportsTest_nullInputStream() {
+ final var modelId = "modelId";
+
+ final OperationException actualOperationException = assertThrows(OperationException.class,
+ () -> modelBusinessLogic.createModelImports(modelId, null));
+
+ final OperationException expectedOperationException = ModelOperationExceptionSupplier.emptyModelImports().get();
+ assertEquals(actualOperationException.getActionStatus(), expectedOperationException.getActionStatus());
+ assertEquals(actualOperationException.getParams().length, expectedOperationException.getParams().length);
+ }
+
+ @Test
+ void createModelImportsTest_emptyModelImports() throws IOException {
+ final var modelId = "modelId";
+
+ final var resolve = modelImportsResourcePath.resolve("emptyModelImports.zip");
+ final var zipBytes = Files.readAllBytes(resolve);
+ final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(zipBytes);
+
+ when(modelOperation.findModelByName(modelId)).thenReturn(Optional.of(new Model(modelId)));
+
+ final OperationException actualOperationException = assertThrows(OperationException.class,
+ () -> modelBusinessLogic.createModelImports(modelId, byteArrayInputStream));
+
+ final OperationException expectedOperationException = ModelOperationExceptionSupplier.emptyModelImports().get();
+ assertEquals(actualOperationException.getActionStatus(), expectedOperationException.getActionStatus());
+ assertEquals(actualOperationException.getParams().length, expectedOperationException.getParams().length);
+ }
+
+ @Test
+ void createModelImportsTest_modelNotFound() {
+ final var modelId = "modelId";
+ final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(new byte[0]);
+
+ when(modelOperation.findModelByName(modelId)).thenReturn(Optional.empty());
+
+ final OperationException actualOperationException = assertThrows(OperationException.class,
+ () -> modelBusinessLogic.createModelImports(modelId, byteArrayInputStream));
+
+ final OperationException expectedOperationException = ModelOperationExceptionSupplier.invalidModel(modelId).get();
+ assertEquals(actualOperationException.getActionStatus(), expectedOperationException.getActionStatus());
+ assertEquals(actualOperationException.getParams().length, expectedOperationException.getParams().length);
+ }
+
+ @Test
+ void findModelSuccessTest() {
+ final var modelId = "modelId";
+ when(modelOperation.findModelByName(modelId)).thenReturn(Optional.of(new Model(modelId)));
+ final Optional<Model> actualModel = modelBusinessLogic.findModel(modelId);
+ assertTrue(actualModel.isPresent());
+ assertEquals(new Model(modelId), actualModel.get());
+ }
+
+ @Test
+ void findModelTest_emptyOrNullModelName() {
+ when(modelOperation.findModelByName(anyString())).thenReturn(Optional.of(new Model()));
+ var actualModel = modelBusinessLogic.findModel("");
+ assertTrue(actualModel.isEmpty());
+ actualModel = modelBusinessLogic.findModel(null);
+ assertTrue(actualModel.isEmpty());
+ }
} \ No newline at end of file
diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/path/beans/ToscaModelImportCassandraDaoMock.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/path/beans/ToscaModelImportCassandraDaoMock.java
new file mode 100644
index 0000000000..9f293b1a1c
--- /dev/null
+++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/path/beans/ToscaModelImportCassandraDaoMock.java
@@ -0,0 +1,39 @@
+/*
+ * ============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.components.path.beans;
+
+import javax.annotation.PostConstruct;
+import org.openecomp.sdc.be.dao.cassandra.CassandraClient;
+import org.openecomp.sdc.be.dao.cassandra.ToscaModelImportCassandraDao;
+import org.springframework.stereotype.Component;
+
+@Component("tosca-model-import-cassandra-dao")
+public class ToscaModelImportCassandraDaoMock extends ToscaModelImportCassandraDao {
+
+ public ToscaModelImportCassandraDaoMock(final CassandraClient cassandraClient) {
+ super(cassandraClient);
+ }
+
+ @PostConstruct
+ @Override
+ public void init() {
+
+ }
+}
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 34201d3c1f..5992be4e9d 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
@@ -18,19 +18,28 @@
*/
package org.openecomp.sdc.be.servlets;
-import static org.assertj.core.api.Assertions.assertThat;
-import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.when;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.io.InputStream;
+import java.nio.file.Path;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
-import javax.validation.ConstraintViolationException;
-import javax.ws.rs.core.Response;
+import javax.ws.rs.client.Entity;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response.Status;
import org.apache.commons.lang3.StringUtils;
import org.eclipse.jetty.http.HttpStatus;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.client.ClientConfig;
+import org.glassfish.jersey.media.multipart.FormDataMultiPart;
+import org.glassfish.jersey.media.multipart.MultiPartFeature;
import org.glassfish.jersey.server.ResourceConfig;
import org.glassfish.jersey.test.JerseyTest;
import org.glassfish.jersey.test.TestProperties;
@@ -46,6 +55,7 @@ import org.mockito.MockitoAnnotations;
import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
import org.openecomp.sdc.be.components.impl.ModelBusinessLogic;
import org.openecomp.sdc.be.components.impl.ResourceImportManager;
+import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
import org.openecomp.sdc.be.components.validation.UserValidations;
import org.openecomp.sdc.be.config.ConfigurationManager;
import org.openecomp.sdc.be.config.SpringConfig;
@@ -55,6 +65,9 @@ import org.openecomp.sdc.be.impl.ComponentsUtils;
import org.openecomp.sdc.be.impl.ServletUtils;
import org.openecomp.sdc.be.impl.WebAppContextWrapper;
import org.openecomp.sdc.be.model.Model;
+import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException;
+import org.openecomp.sdc.be.servlets.builder.ServletResponseBuilder;
+import org.openecomp.sdc.be.servlets.exception.OperationExceptionMapper;
import org.openecomp.sdc.be.ui.model.ModelCreateRequest;
import org.openecomp.sdc.be.user.UserBusinessLogic;
import org.openecomp.sdc.common.api.ConfigurationSource;
@@ -100,13 +113,16 @@ class ModelServletTest extends JerseyTest {
@Mock
private UserValidations userValidations;
+ @Mock
+ private ResponseFormatManager responseFormatManager;
+
private Model model;
- private Response response;
private ModelCreateRequest modelCreateRequest;
+ private final Path rootPath = Path.of("/v1/catalog/model");
+ private final Path importsPath = rootPath.resolve("imports");
@BeforeAll
public void initClass() {
- MockitoAnnotations.openMocks(this);
when(request.getSession()).thenReturn(session);
when(session.getServletContext()).thenReturn(servletContext);
when(servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR))
@@ -145,6 +161,7 @@ class ModelServletTest extends JerseyTest {
@Override
protected ResourceConfig configure() {
+ MockitoAnnotations.openMocks(this);
forceSet(TestProperties.CONTAINER_PORT, "0");
final ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
return new ResourceConfig(ModelServlet.class)
@@ -158,51 +175,132 @@ class ModelServletTest extends JerseyTest {
bind(servletUtils).to(ServletUtils.class);
bind(resourceImportManager).to(ResourceImportManager.class);
bind(modelBusinessLogic).to(ModelBusinessLogic.class);
+ bind(userValidations).to(UserValidations.class);
}
})
+ .register(new OperationExceptionMapper(new ServletResponseBuilder(), responseFormatManager))
+ .register(MultiPartFeature.class)
.property("contextConfig", context);
}
+ @Override
+ protected void configureClient(final ClientConfig config) {
+ config.register(MultiPartFeature.class);
+ }
+
@Test
- void createModelSuccessTest() {
+ void createModelSuccessTest() throws JsonProcessingException {
when(responseFormat.getStatus()).thenReturn(HttpStatus.OK_200);
when(componentsUtils.getResponseFormat(ActionStatus.CREATED)).thenReturn(responseFormat);
when(modelBusinessLogic.createModel(any(Model.class))).thenReturn(model);
- response = modelServlet.createModel(modelCreateRequest, USER_ID);
- assertThat(response.getStatus()).isEqualTo(HttpStatus.OK_200);
+ final FormDataMultiPart formDataMultiPart = buildCreateFormDataMultiPart(new byte[0], parseToJsonString(modelCreateRequest));
+ 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() {
+ void createModelFailTest() throws JsonProcessingException {
when(responseFormat.getStatus()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR_500);
when(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR)).thenReturn(responseFormat);
when(modelBusinessLogic.createModel(any(Model.class))).thenReturn(model);
- response = modelServlet.createModel(modelCreateRequest, USER_ID);
- assertThat(response.getStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR_500);
+ final FormDataMultiPart formDataMultiPart = buildCreateFormDataMultiPart(new byte[0], parseToJsonString(modelCreateRequest));
+ 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.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
}
@Test
- void createModelFailWithModelNameEmptyTest() {
+ void createModelFailWithModelNameEmptyTest() throws JsonProcessingException {
when(responseFormat.getStatus()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR_500);
when(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR)).thenReturn(responseFormat);
modelCreateRequest.setName(StringUtils.EMPTY);
- final Exception exception = assertThrows(ConstraintViolationException.class, () -> modelServlet.createModel(modelCreateRequest, USER_ID));
- assertThat(exception.getMessage()).isEqualTo("Model name cannot be empty");
+ final FormDataMultiPart formDataMultiPart = buildCreateFormDataMultiPart(new byte[0], parseToJsonString(modelCreateRequest));
+ 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.BAD_REQUEST.getStatusCode(), response.getStatus());
}
@Test
- void createModelFailWithModelNameNullTest() {
+ void createModelFailWithModelNameNullTest() throws JsonProcessingException {
when(responseFormat.getStatus()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR_500);
when(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR)).thenReturn(responseFormat);
modelCreateRequest.setName(null);
- final Exception exception = assertThrows(ConstraintViolationException.class, () -> modelServlet.createModel(modelCreateRequest, USER_ID));
- assertThat(exception.getMessage()).isEqualTo("Model name cannot be null");
+ final var modelFile = new byte[0];
+ final FormDataMultiPart formDataMultiPart = buildCreateFormDataMultiPart(modelFile, parseToJsonString(modelCreateRequest));
+ 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.BAD_REQUEST.getStatusCode(), response.getStatus());
}
@Test
- void createModelThrowsBusinessExceptionTest() {
+ void createModelThrowsBusinessExceptionTest() throws JsonProcessingException {
+ final var modelFile = new byte[0];
+ final String modelCreateAsJson = parseToJsonString(modelCreateRequest);
+ final FormDataMultiPart formDataMultiPart = buildCreateFormDataMultiPart(modelFile, modelCreateAsJson);
when(modelBusinessLogic.createModel(model)).thenThrow(new BusinessException() {});
- assertThrows(BusinessException.class, () -> modelServlet.createModel(modelCreateRequest, USER_ID));
+
+ 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.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
+ }
+
+ @Test
+ void updateModelImportsSuccessTest() {
+ final FormDataMultiPart formDataMultiPart = buildUpdateFormDataMultiPart("model1", new byte[0]);
+
+ final var response = target(importsPath.toString()).request(MediaType.APPLICATION_JSON)
+ .header(Constants.USER_ID_HEADER, USER_ID)
+ .put(Entity.entity(formDataMultiPart, MediaType.MULTIPART_FORM_DATA));
+ assertEquals(Status.NO_CONTENT.getStatusCode(), response.getStatus());
+ }
+
+ @Test
+ void updateModelImports_businessException() {
+ final var modelId = "model1";
+ final FormDataMultiPart formDataMultiPart = buildUpdateFormDataMultiPart(modelId, new byte[0]);
+ final OperationException operationException = new OperationException(ActionStatus.INVALID_MODEL, modelId);
+ doThrow(operationException).when(modelBusinessLogic).createModelImports(eq(modelId), any(InputStream.class));
+ when(responseFormatManager.getResponseFormat(ActionStatus.INVALID_MODEL, modelId))
+ .thenReturn(new ResponseFormat(Status.BAD_REQUEST.getStatusCode()));
+ final var response = target(importsPath.toString()).request(MediaType.APPLICATION_JSON)
+ .header(Constants.USER_ID_HEADER, USER_ID)
+ .put(Entity.entity(formDataMultiPart, MediaType.MULTIPART_FORM_DATA));
+ assertEquals(Status.BAD_REQUEST.getStatusCode(), response.getStatus());
+ }
+
+ @Test
+ void updateModelImports_unknownException() {
+ final var modelName = "model1";
+ final FormDataMultiPart formDataMultiPart = buildUpdateFormDataMultiPart(modelName, new byte[0]);
+ doThrow(new RuntimeException()).when(modelBusinessLogic).createModelImports(eq(modelName), any(InputStream.class));
+ when(responseFormat.getStatus()).thenReturn(HttpStatus.INTERNAL_SERVER_ERROR_500);
+ when(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR)).thenReturn(responseFormat);
+ final var response = target(importsPath.toString()).request(MediaType.APPLICATION_JSON)
+ .header(Constants.USER_ID_HEADER, USER_ID)
+ .put(Entity.entity(formDataMultiPart, MediaType.MULTIPART_FORM_DATA));
+ assertEquals(Status.INTERNAL_SERVER_ERROR.getStatusCode(), response.getStatus());
+ }
+
+ private FormDataMultiPart buildUpdateFormDataMultiPart(final String modelName, final byte[] importFilesZip) {
+ return new FormDataMultiPart()
+ .field("modelName", modelName)
+ .field("modelImportsZip", importFilesZip, MediaType.MULTIPART_FORM_DATA_TYPE);
+ }
+
+ private FormDataMultiPart buildCreateFormDataMultiPart(final byte[] modelFile, final String modelCreateAsJson) {
+ return new FormDataMultiPart()
+ .field("model", modelCreateAsJson, MediaType.APPLICATION_JSON_TYPE)
+ .field("modelImportsZip", modelFile, MediaType.MULTIPART_FORM_DATA_TYPE);
+ }
+
+ private String parseToJsonString(final Object object) throws JsonProcessingException {
+ return new ObjectMapper().writeValueAsString(object);
}
} \ No newline at end of file
diff --git a/catalog-be/src/test/resources/modelImports/emptyModelImports.zip b/catalog-be/src/test/resources/modelImports/emptyModelImports.zip
new file mode 100644
index 0000000000..323472535d
--- /dev/null
+++ b/catalog-be/src/test/resources/modelImports/emptyModelImports.zip
Binary files differ
diff --git a/catalog-be/src/test/resources/modelImports/modelWithSubFolderAndEmptyFolder.zip b/catalog-be/src/test/resources/modelImports/modelWithSubFolderAndEmptyFolder.zip
new file mode 100644
index 0000000000..281e3fc3c1
--- /dev/null
+++ b/catalog-be/src/test/resources/modelImports/modelWithSubFolderAndEmptyFolder.zip
Binary files differ
diff --git a/catalog-be/src/test/resources/paths/path-context.xml b/catalog-be/src/test/resources/paths/path-context.xml
index 3b472dd16f..7994b0c44d 100644
--- a/catalog-be/src/test/resources/paths/path-context.xml
+++ b/catalog-be/src/test/resources/paths/path-context.xml
@@ -80,6 +80,7 @@ Modifications copyright (c) 2018 Nokia
<bean id="userOperation" class="org.openecomp.sdc.be.facade.operations.UserOperation"/>
<bean id="dmaapProducerHealth" class="org.openecomp.sdc.be.catalog.impl.DmaapProducerHealth"/>
<bean id="feature_toggle_dao" class="org.openecomp.sdc.be.components.path.beans.FeatureToggleDaoMock"/>
+ <bean id="tosca-model-import-cassandra-dao" class="org.openecomp.sdc.be.components.path.beans.ToscaModelImportCassandraDaoMock"/>
<bean name="httpClient" class="org.apache.http.impl.client.HttpClients" factory-method="createDefault" />
<bean class="org.openecomp.sdc.be.components.attribute.AttributeDeclarationOrchestrator"/>
<bean class="org.openecomp.sdc.be.components.attribute.ComponentInstanceOutputAttributeDeclarator"/>