Add models imports endpoint and persistence structure 87/121687/4
authorandre.schmid <andre.schmid@est.tech>
Fri, 14 May 2021 19:38:45 +0000 (20:38 +0100)
committerChristophe Closset <christophe.closset@intl.att.com>
Mon, 14 Jun 2021 08:16:08 +0000 (08:16 +0000)
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>
27 files changed:
catalog-be/pom.xml
catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml
catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogic.java
catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ModelServlet.java
catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/OperationExceptionMapper.java
catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogicTest.java
catalog-be/src/test/java/org/openecomp/sdc/be/components/path/beans/ToscaModelImportCassandraDaoMock.java [new file with mode: 0644]
catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ModelServletTest.java
catalog-be/src/test/resources/modelImports/emptyModelImports.zip [new file with mode: 0644]
catalog-be/src/test/resources/modelImports/modelWithSubFolderAndEmptyFolder.zip [new file with mode: 0644]
catalog-be/src/test/resources/paths/path-context.xml
catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java
catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/exception/CassandraDaoInitException.java [new file with mode: 0644]
catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/exception/CassandraDaoInitExceptionProvider.java [new file with mode: 0644]
catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/CassandraClient.java
catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ToscaImportByModelAccessor.java [new file with mode: 0644]
catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDao.java [new file with mode: 0644]
catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/SdcSchemaBuilder.java
catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/Table.java
catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ToscaImportByModelTableDescription.java [new file with mode: 0644]
catalog-dao/src/test/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDaoTest.java [new file with mode: 0644]
catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/exception/ModelOperationExceptionSupplier.java [new file with mode: 0644]
catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java
catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java
common-be/src/main/java/org/openecomp/sdc/be/data/model/ToscaImportByModel.java [new file with mode: 0644]
common/onap-tosca-datatype/src/main/java/org/onap/sdc/tosca/datatypes/model/CapabilityDefinition.java
common/onap-tosca-datatype/src/main/java/org/onap/sdc/tosca/datatypes/model/EntrySchema.java

index 3f56c02..53af2c7 100644 (file)
                   </outputDirectory>
                   <resources>
                     <resource>
-                      <directory>${project.parent.basedir}/catalog-be/target
-                      </directory>
+                      <directory>${project.build.directory}</directory>
                       <includes>
                         <include>normatives.tar.gz</include>
                       </includes>
index 2c6a0c8..b277aee 100644 (file)
@@ -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"
+    }
index 1ef4cef..7f68a00 100644 (file)
  */
 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
index f4fc883..0c5e4ae 100644 (file)
@@ -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) {
index 7c25f8a..062e03b 100644 (file)
@@ -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()));
     }
 }
index 6c7bd53..b88bdfb 100644 (file)
 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 (file)
index 0000000..9f293b1
--- /dev/null
@@ -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() {
+
+    }
+}
index 34201d3..5992be4 100644 (file)
  */
 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 (file)
index 0000000..3234725
Binary files /dev/null and b/catalog-be/src/test/resources/modelImports/emptyModelImports.zip 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 (file)
index 0000000..281e3fc
Binary files /dev/null and b/catalog-be/src/test/resources/modelImports/modelWithSubFolderAndEmptyFolder.zip differ
index 3b472dd..7994b0c 100644 (file)
@@ -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"/>
index 1ebaeab..ed448c1 100644 (file)
@@ -52,7 +52,7 @@ public enum ActionStatus {
     // Category related
     COMPONENT_MISSING_CATEGORY, COMPONENT_INVALID_CATEGORY, COMPONENT_ELEMENT_INVALID_NAME_FORMAT, COMPONENT_ELEMENT_INVALID_NAME_LENGTH, COMPONENT_CATEGORY_ALREADY_EXISTS, COMPONENT_CATEGORY_NOT_FOUND, COMPONENT_SUB_CATEGORY_NOT_FOUND_FOR_CATEGORY, COMPONENT_SUB_CATEGORY_EXISTS_FOR_CATEGORY, COMPONENT_GROUPING_EXISTS_FOR_SUB_CATEGORY,
     // Model related
-    MODEL_ALREADY_EXISTS,
+    MODEL_ALREADY_EXISTS, INVALID_MODEL, MODEL_IMPORTS_IS_EMPTY, COULD_NOT_READ_MODEL_IMPORTS,
     // Service API URL
     INVALID_SERVICE_API_URL,
     // Property related
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/exception/CassandraDaoInitException.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/exception/CassandraDaoInitException.java
new file mode 100644 (file)
index 0000000..b1ee5f0
--- /dev/null
@@ -0,0 +1,27 @@
+/*
+ * ============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.dao.api.exception;
+
+public class CassandraDaoInitException extends RuntimeException {
+
+    public CassandraDaoInitException(final String message) {
+        super(message);
+    }
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/exception/CassandraDaoInitExceptionProvider.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/exception/CassandraDaoInitExceptionProvider.java
new file mode 100644 (file)
index 0000000..4fec8fb
--- /dev/null
@@ -0,0 +1,36 @@
+/*
+ * ============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.dao.api.exception;
+
+import java.util.function.Supplier;
+import org.openecomp.sdc.be.dao.cassandra.CassandraOperationStatus;
+
+public class CassandraDaoInitExceptionProvider {
+
+    private CassandraDaoInitExceptionProvider() {
+
+    }
+
+    public static Supplier<CassandraDaoInitException> keySpaceConnectError(final String keyspace, final CassandraOperationStatus cassandraOperationStatus) {
+        var errorMsg = String.format("Could not connect to keyspace '%s'. Operation status was '%s'", keyspace, cassandraOperationStatus);
+        return () -> new CassandraDaoInitException(errorMsg);
+    }
+
+}
index 624f9b4..25a6b49 100644 (file)
@@ -34,6 +34,7 @@ import java.util.List;
 import javax.annotation.PreDestroy;
 import org.apache.commons.lang3.tuple.ImmutablePair;
 import org.openecomp.sdc.be.config.ConfigurationManager;
+import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
 import org.openecomp.sdc.common.log.wrappers.Logger;
 import org.springframework.stereotype.Component;
 
@@ -155,7 +156,7 @@ public class CassandraClient {
             Mapper<T> mapper = manager.mapper(clazz);
             mapper.save(entity);
         } catch (Exception e) {
-            logger.debug("Failed to save entity [{}], error :", entity, e);
+            logger.error(EcompLoggerErrorCode.DATA_ERROR, CassandraClient.class.getName(), "Failed to save entity [{}], error :", entity, e);
             return CassandraOperationStatus.GENERAL_ERROR;
         }
         return CassandraOperationStatus.OK;
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ToscaImportByModelAccessor.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ToscaImportByModelAccessor.java
new file mode 100644 (file)
index 0000000..eb54bd7
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * ============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.dao.cassandra;
+
+import com.datastax.driver.mapping.Result;
+import com.datastax.driver.mapping.annotations.Accessor;
+import com.datastax.driver.mapping.annotations.Param;
+import com.datastax.driver.mapping.annotations.Query;
+import org.openecomp.sdc.be.data.model.ToscaImportByModel;
+
+@Accessor
+public interface ToscaImportByModelAccessor {
+
+    @Query("SELECT * FROM sdcartifact.tosca_import_by_model WHERE model_id = :modelId")
+    Result<ToscaImportByModel> findAllByModel(@Param("modelId") String modelId);
+}
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDao.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDao.java
new file mode 100644 (file)
index 0000000..a8b1ec6
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * ============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.dao.cassandra;
+
+import static java.util.function.Predicate.not;
+
+import com.datastax.driver.core.Session;
+import com.datastax.driver.mapping.Mapper;
+import com.datastax.driver.mapping.MappingManager;
+import fj.data.Either;
+import java.util.List;
+import java.util.stream.Collectors;
+import javax.annotation.PostConstruct;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.api.exception.CassandraDaoInitException;
+import org.openecomp.sdc.be.dao.api.exception.CassandraDaoInitExceptionProvider;
+import org.openecomp.sdc.be.data.model.ToscaImportByModel;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
+import org.openecomp.sdc.common.log.wrappers.Logger;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+@Component("tosca-model-import-cassandra-dao")
+public class ToscaModelImportCassandraDao extends CassandraDao {
+
+    private static final Logger LOGGER = Logger.getLogger(ToscaModelImportCassandraDao.class.getName());
+
+    private ToscaImportByModelAccessor toscaImportByModelAccessor;
+    private Mapper<ToscaImportByModel> toscaImportByModelMapper;
+
+    @Autowired
+    public ToscaModelImportCassandraDao(final CassandraClient cassandraClient) {
+        super(cassandraClient);
+    }
+
+    /**
+     * For test purposes.
+     *
+     * @param toscaImportByModelAccessor the sdcartifact.tosca_import_by_model accessor
+     */
+    ToscaModelImportCassandraDao(final ToscaImportByModelAccessor toscaImportByModelAccessor,
+                                 final Mapper<ToscaImportByModel> toscaImportByModelMapper) {
+        super(null);
+        this.toscaImportByModelAccessor = toscaImportByModelAccessor;
+        this.toscaImportByModelMapper = toscaImportByModelMapper;
+    }
+
+    @PostConstruct
+    public void init() {
+        final var keyspace = AuditingTypesConstants.ARTIFACT_KEYSPACE;
+        if (!client.isConnected()) {
+            LOGGER.error(EcompLoggerErrorCode.SCHEMA_ERROR, ToscaModelImportCassandraDao.class.getName(), "Cassandra client isn't connected");
+            return;
+        }
+        final Either<ImmutablePair<Session, MappingManager>, CassandraOperationStatus> connectionResult = client.connect(keyspace);
+        if (connectionResult.isRight()) {
+            final CassandraDaoInitException exception =
+                CassandraDaoInitExceptionProvider.keySpaceConnectError(keyspace, connectionResult.right().value()).get();
+            LOGGER.error(EcompLoggerErrorCode.SCHEMA_ERROR, ToscaModelImportCassandraDao.class.getName(), exception.getMessage());
+            throw exception;
+        }
+        session = connectionResult.left().value().getLeft();
+        manager = connectionResult.left().value().getRight();
+        toscaImportByModelMapper = manager.mapper(ToscaImportByModel.class);
+        toscaImportByModelAccessor = manager.createAccessor(ToscaImportByModelAccessor.class);
+        LOGGER.info("{} successfully initialized", ToscaModelImportCassandraDao.class.getName());
+    }
+
+    public void importAll(final String modelId, final List<ToscaImportByModel> toscaImportByModelList) {
+        final List<ToscaImportByModel> importOfModelList = toscaImportByModelList.stream()
+            .filter(toscaImportByModel -> modelId.equals(toscaImportByModel.getModelId()))
+            .collect(Collectors.toList());
+        final List<ToscaImportByModel> actualImportOfModelList = toscaImportByModelAccessor.findAllByModel(modelId).all();
+        final List<ToscaImportByModel> removedImportList = actualImportOfModelList.stream()
+            .filter(not(importOfModelList::contains))
+            .collect(Collectors.toList());
+
+        importOfModelList.forEach(toscaImportByModelMapper::save);
+        removedImportList.forEach(toscaImportByModel ->
+            toscaImportByModelMapper.delete(toscaImportByModel.getModelId(), toscaImportByModel.getFullPath())
+        );
+    }
+
+    public List<ToscaImportByModel> findAllByModel(final String modelId) {
+        return toscaImportByModelAccessor.findAllByModel(modelId).all();
+    }
+
+}
index 0c6bb45..c0c12aa 100644 (file)
@@ -31,10 +31,12 @@ import com.datastax.driver.core.schemabuilder.Alter;
 import com.datastax.driver.core.schemabuilder.Create;
 import com.datastax.driver.core.schemabuilder.SchemaBuilder;
 import com.datastax.driver.core.schemabuilder.SchemaStatement;
+import com.datastax.oss.driver.shaded.guava.common.base.Function;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Map.Entry;
 import java.util.Optional;
 import java.util.function.Supplier;
 import java.util.stream.Collectors;
@@ -112,17 +114,17 @@ public class SdcSchemaBuilder {
      * the method creats all the tables and indexes thet do not already exist
      *
      * @param iTableDescriptions:    a list of table description we want to create
-     * @param keyspaceMetadate:      the current tables that exist in the cassandra under this keyspace
+     * @param keyspaceMetadata:      the current tables that exist in the cassandra under this keyspace
      * @param session:               the session object used for the execution of the query.
      * @param existingTablesMetadata the current tables columns that exist in the cassandra under this keyspace
      */
-    private static void createTables(List<ITableDescription> iTableDescriptions, Map<String, List<String>> keyspaceMetadate, Session session,
+    private static void createTables(List<ITableDescription> iTableDescriptions, Map<String, List<String>> keyspaceMetadata, Session session,
                                      Map<String, List<String>> existingTablesMetadata) {
         for (ITableDescription tableDescription : iTableDescriptions) {
             String tableName = tableDescription.getTableName().toLowerCase();
             Map<String, ImmutablePair<DataType, Boolean>> columnDescription = tableDescription.getColumnDescription();
             log.info("creating tables:{}.", tableName);
-            if (keyspaceMetadate == null || !keyspaceMetadate.keySet().contains(tableName)) {
+            if (keyspaceMetadata == null || !keyspaceMetadata.containsKey(tableName)) {
                 Create create = SchemaBuilder.createTable(tableDescription.getKeyspace(), tableDescription.getTableName());
                 for (ImmutablePair<String, DataType> key : tableDescription.primaryKeys()) {
                     create.addPartitionKey(key.getLeft(), key.getRight());
@@ -132,9 +134,15 @@ public class SdcSchemaBuilder {
                         create.addClusteringColumn(key.getLeft(), key.getRight());
                     }
                 }
-                for (Map.Entry<String, ImmutablePair<DataType, Boolean>> entry : columnDescription.entrySet()) {
-                    create.addColumn(entry.getKey(), entry.getValue().getLeft());
-                }
+                final Function<Entry<String, ?>, Boolean> notPrimaryKeyFilter = (Entry<String, ?> entry) -> {
+                    if (entry == null) {
+                        return true;
+                    }
+                    return tableDescription.primaryKeys().stream().noneMatch(primaryKeyPair -> primaryKeyPair.getLeft().equals(entry.getKey()));
+                };
+                columnDescription.entrySet().stream()
+                    .filter(notPrimaryKeyFilter::apply)
+                    .forEach(entry -> create.addColumn(entry.getKey(), entry.getValue().getLeft()));
                 log.trace("exacuting :{}", create);
                 session.execute(create);
                 log.info("table:{} created successfully.", tableName);
@@ -142,8 +150,8 @@ public class SdcSchemaBuilder {
                 log.info("table:{} already exists, skipping.", tableName);
                 alterTable(session, existingTablesMetadata, tableDescription, tableName, columnDescription);
             }
-            log.info("keyspacemetadata:{}", keyspaceMetadate);
-            List<String> indexNames = (keyspaceMetadate != null && keyspaceMetadate.get(tableName) != null ? keyspaceMetadate.get(tableName)
+            log.info("keyspacemetadata:{}", keyspaceMetadata);
+            List<String> indexNames = (keyspaceMetadata != null && keyspaceMetadata.get(tableName) != null ? keyspaceMetadata.get(tableName)
                 : new ArrayList<>());
             log.info("table:{} creating indexes.", tableName);
             for (Map.Entry<String, ImmutablePair<DataType, Boolean>> description : columnDescription.entrySet()) {
index 4c3c8fa..710a9a2 100644 (file)
@@ -37,6 +37,7 @@ import org.openecomp.sdc.be.dao.cassandra.schema.tables.MigrationTasksTableDescr
 import org.openecomp.sdc.be.dao.cassandra.schema.tables.OperationalEnvironmentsTableDescription;
 import org.openecomp.sdc.be.dao.cassandra.schema.tables.ResAdminEventTableDescription;
 import org.openecomp.sdc.be.dao.cassandra.schema.tables.SdcSchemaFilesTableDescription;
+import org.openecomp.sdc.be.dao.cassandra.schema.tables.ToscaImportByModelTableDescription;
 import org.openecomp.sdc.be.dao.cassandra.schema.tables.UserAccessEventTableDescription;
 import org.openecomp.sdc.be.dao.cassandra.schema.tables.UserAdminEventTableDescription;
 
@@ -61,7 +62,8 @@ public enum Table {
     SDC_REPO(new MigrationTasksTableDescription()),
     SDC_OPERATIONAL_ENVIRONMENT(new OperationalEnvironmentsTableDescription()),
     AUDIT_ECOMP_OPERATIONAL_ENVIRONMENT(new EcompOperationalEnvironmentEventTableDesc()),
-    FEATURE_TOGGLE_STATE(new FeatureToggleEventTableDesc());
+    FEATURE_TOGGLE_STATE(new FeatureToggleEventTableDesc()),
+    TOSCA_IMPORT_BY_MODEL(new ToscaImportByModelTableDescription());
     // @formatter:on
 
     ITableDescription tableDescription;
diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ToscaImportByModelTableDescription.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/cassandra/schema/tables/ToscaImportByModelTableDescription.java
new file mode 100644 (file)
index 0000000..5b67136
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * ============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.dao.cassandra.schema.tables;
+
+import com.datastax.driver.core.DataType;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.openecomp.sdc.be.dao.cassandra.schema.ITableDescription;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+public class ToscaImportByModelTableDescription implements ITableDescription {
+
+    private static final String MODEL_ID = "model_id";
+    private static final String FULL_PATH = "full_path";
+
+    @Override
+    public List<ImmutablePair<String, DataType>> primaryKeys() {
+        return List.of(
+            new ImmutablePair<>(MODEL_ID, DataType.varchar()),
+            new ImmutablePair<>(FULL_PATH, DataType.varchar())
+        );
+    }
+
+    @Override
+    public List<ImmutablePair<String, DataType>> clusteringKeys() {
+        return Collections.emptyList();
+    }
+
+    @Override
+    public Map<String, ImmutablePair<DataType, Boolean>> getColumnDescription() {
+        return Stream.of(SdcSchemaFilesFieldsDescription.values())
+            .collect(Collectors.toMap(SdcSchemaFilesFieldsDescription::getName, field -> new ImmutablePair<>(field.type, field.indexed)));
+    }
+
+    @Override
+    public String getKeyspace() {
+        return AuditingTypesConstants.ARTIFACT_KEYSPACE;
+    }
+
+    @Override
+    public String getTableName() {
+        return "tosca_import_by_model";
+    }
+
+    @Getter
+    @AllArgsConstructor
+    enum SdcSchemaFilesFieldsDescription {
+        MODEL_ID("model_id", DataType.varchar(), true),
+        CONTENT("content", DataType.varchar(), false);
+
+        private final String name;
+        private final DataType type;
+        private final boolean indexed;
+    }
+}
diff --git a/catalog-dao/src/test/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDaoTest.java b/catalog-dao/src/test/java/org/openecomp/sdc/be/dao/cassandra/ToscaModelImportCassandraDaoTest.java
new file mode 100644 (file)
index 0000000..cddf3a2
--- /dev/null
@@ -0,0 +1,152 @@
+/*
+ * ============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.dao.cassandra;
+
+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.anyString;
+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 com.datastax.driver.core.Session;
+import com.datastax.driver.mapping.Mapper;
+import com.datastax.driver.mapping.MappingManager;
+import com.datastax.driver.mapping.Result;
+import fj.data.Either;
+import java.util.List;
+import org.apache.commons.lang3.tuple.ImmutablePair;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.InjectMocks;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.openecomp.sdc.be.dao.api.exception.CassandraDaoInitException;
+import org.openecomp.sdc.be.dao.api.exception.CassandraDaoInitExceptionProvider;
+import org.openecomp.sdc.be.data.model.ToscaImportByModel;
+import org.openecomp.sdc.be.resources.data.auditing.AuditingTypesConstants;
+
+class ToscaModelImportCassandraDaoTest {
+
+    @Mock
+    private CassandraClient cassandraClient;
+
+    @InjectMocks
+    private ToscaModelImportCassandraDao toscaModelImportCassandraDao;
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        MockitoAnnotations.openMocks(this);
+    }
+
+    @Test
+    void findAllByModelTest() {
+        final var toscaImportByModelAccessorMock = mock(ToscaImportByModelAccessor.class);
+        toscaModelImportCassandraDao = new ToscaModelImportCassandraDao(toscaImportByModelAccessorMock, null);
+        final var modelId = "modelId";
+        final ToscaImportByModel toscaImportByModel1 = new ToscaImportByModel();
+        final ToscaImportByModel toscaImportByModel2 = new ToscaImportByModel();
+        final List<ToscaImportByModel> expectedImportModelList = List.of(toscaImportByModel1, toscaImportByModel2);
+
+        final Result<ToscaImportByModel> result = mock(Result.class);
+        when(result.all()).thenReturn(expectedImportModelList);
+        when(toscaImportByModelAccessorMock.findAllByModel(modelId)).thenReturn(result);
+
+        final List<ToscaImportByModel> actualImportModelList = toscaModelImportCassandraDao.findAllByModel(modelId);
+        assertEquals(expectedImportModelList.size(), actualImportModelList.size());
+        assertTrue(actualImportModelList.contains(toscaImportByModel1));
+        assertTrue(actualImportModelList.contains(toscaImportByModel2));
+    }
+
+    @Test
+    void importAllTest() {
+        final var toscaImportByModelAccessorMock = mock(ToscaImportByModelAccessor.class);
+        final Mapper<ToscaImportByModel> toscaImportByModelMapperMock = mock(Mapper.class);
+        toscaModelImportCassandraDao = new ToscaModelImportCassandraDao(toscaImportByModelAccessorMock, toscaImportByModelMapperMock);
+
+        final var modelId = "modelId";
+        final var toscaImportByModel1 = createToscaModelByImport(modelId, "path/model/1");
+        final var toscaImportByModel2 = createToscaModelByImport(modelId, "path/model/2");
+        final var toscaImportByModel3 = createToscaModelByImport(modelId, "path/model/3");
+        final var toscaImportByModelWrongModel = createToscaModelByImport("otherModel", "path/wrong-model/1");
+        final var importModelList = List.of(toscaImportByModel1, toscaImportByModel2, toscaImportByModel3);
+
+        final var toscaImportByModelDatabase1 = createToscaModelByImport(modelId, "toscaImportByModelDatabase1");
+        final Result<ToscaImportByModel> findAllByModelResult = mock(Result.class);
+        when(findAllByModelResult.all()).thenReturn(List.of(toscaImportByModel1, toscaImportByModelDatabase1));
+        when(toscaImportByModelAccessorMock.findAllByModel(modelId)).thenReturn(findAllByModelResult);
+
+        toscaModelImportCassandraDao.importAll(modelId, importModelList);
+
+        verify(toscaImportByModelMapperMock).save(toscaImportByModel1);
+        verify(toscaImportByModelMapperMock).save(toscaImportByModel2);
+        verify(toscaImportByModelMapperMock).save(toscaImportByModel3);
+        verify(toscaImportByModelMapperMock, never()).save(toscaImportByModelWrongModel);
+        verify(toscaImportByModelMapperMock).delete(toscaImportByModelDatabase1.getModelId(), toscaImportByModelDatabase1.getFullPath());
+        verify(toscaImportByModelMapperMock, never()).delete(toscaImportByModel1.getModelId(), toscaImportByModel1.getFullPath());
+        verify(toscaImportByModelMapperMock, never()).delete(toscaImportByModel2.getModelId(), toscaImportByModel2.getFullPath());
+        verify(toscaImportByModelMapperMock, never()).delete(toscaImportByModel3.getModelId(), toscaImportByModel3.getFullPath());
+        verify(toscaImportByModelMapperMock, never()).delete(toscaImportByModelWrongModel.getModelId(), toscaImportByModelWrongModel.getFullPath());
+    }
+
+    @Test
+    void initSuccessTest() {
+        toscaModelImportCassandraDao = new ToscaModelImportCassandraDao(cassandraClient);
+        when(cassandraClient.isConnected()).thenReturn(true);
+        final Session sessionMock = mock(Session.class);
+        final MappingManager mappingManagerMock = mock(MappingManager.class);
+        when(cassandraClient.connect(AuditingTypesConstants.ARTIFACT_KEYSPACE)).thenReturn(Either.left(new ImmutablePair<>(sessionMock, mappingManagerMock)));
+        toscaModelImportCassandraDao.init();
+        verify(cassandraClient).connect(AuditingTypesConstants.ARTIFACT_KEYSPACE);
+        verify(mappingManagerMock).mapper(ToscaImportByModel.class);
+        verify(mappingManagerMock).createAccessor(ToscaImportByModelAccessor.class);
+    }
+
+    @Test
+    void initTest_clientNotConnected() {
+        toscaModelImportCassandraDao = new ToscaModelImportCassandraDao(cassandraClient);
+        when(cassandraClient.isConnected()).thenReturn(false);
+        toscaModelImportCassandraDao.init();
+        verify(cassandraClient, never()).connect(anyString());
+    }
+
+    @Test
+    void initTest_keyspaceConnectionFailure() {
+        toscaModelImportCassandraDao = new ToscaModelImportCassandraDao(cassandraClient);
+        when(cassandraClient.isConnected()).thenReturn(true);
+        when(cassandraClient.connect(AuditingTypesConstants.ARTIFACT_KEYSPACE))
+            .thenReturn(Either.right(CassandraOperationStatus.KEYSPACE_NOT_CONNECTED));
+
+        final CassandraDaoInitException actualException = assertThrows(CassandraDaoInitException.class, () -> toscaModelImportCassandraDao.init());
+
+        final CassandraDaoInitException expectedException = CassandraDaoInitExceptionProvider
+            .keySpaceConnectError(AuditingTypesConstants.ARTIFACT_KEYSPACE, CassandraOperationStatus.KEYSPACE_NOT_CONNECTED).get();
+        assertEquals(expectedException.getMessage(), actualException.getMessage());
+    }
+
+    private ToscaImportByModel createToscaModelByImport(final String modelId, final String path) {
+        final var toscaImportByModel3 = new ToscaImportByModel();
+        toscaImportByModel3.setModelId(modelId);
+        toscaImportByModel3.setFullPath(path);
+        return toscaImportByModel3;
+    }
+}
\ No newline at end of file
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 (file)
index 0000000..c2ad071
--- /dev/null
@@ -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<OperationException> invalidModel(final String modelName) {
+        return () -> new OperationException(ActionStatus.INVALID_MODEL, modelName);
+    }
+
+    public static Supplier<OperationException> emptyModelImports() {
+        return () -> new OperationException(ActionStatus.MODEL_IMPORTS_IS_EMPTY);
+    }
+
+    public static Supplier<OperationException> couldNotReadImports() {
+        return () -> new OperationException(ActionStatus.COULD_NOT_READ_MODEL_IMPORTS);
+    }
+
+    public static Supplier<OperationException> modelAlreadyExists(final String modelName) {
+        return () -> new OperationException(ActionStatus.MODEL_ALREADY_EXISTS, modelName);
+    }
+
+}
index c604df6..ccc18e5 100644 (file)
 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<ModelData, JanusGraphOperationStatus> createNode = genericDao.createNode(modelData, ModelData.class);
+            final Either<ModelData, JanusGraphOperationStatus> 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<GraphVertex> findModelVertexByName(final String name) {
+        if (StringUtils.isEmpty(name)) {
+            return Optional.empty();
+        }
+        final Map<GraphPropertyEnum, Object> props = new EnumMap<>(GraphPropertyEnum.class);
+        props.put(GraphPropertyEnum.NAME, name);
+        props.put(GraphPropertyEnum.UNIQUE_ID, UniqueIdBuilder.buildModelUid(name));
+        final Either<List<GraphVertex>, 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<Model> findModelByName(final String name) {
+        if (StringUtils.isEmpty(name)) {
+            return Optional.empty();
+        }
+        final Optional<GraphVertex> 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<String, byte[]> zipContent) {
+        if (MapUtils.isEmpty(zipContent)) {
+            return;
+        }
+        final List<ToscaImportByModel> 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);
+    }
 }
 
 
index c1c0132..620c7f7 100644 (file)
 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<String, byte[]> 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<ToscaImportByModel> 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<Map<GraphPropertyEnum, Object>> 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<GraphVertex> modelVertexByNameOpt = modelOperation.findModelVertexByName(modelName);
+        assertTrue(modelVertexByNameOpt.isPresent());
+        assertEquals(expectedVertex, modelVertexByNameOpt.get());
+        final Map<GraphPropertyEnum, Object> value = mapArgumentCaptor.getValue();
+        assertEquals(modelName, value.get(GraphPropertyEnum.NAME));
+        assertEquals(UniqueIdBuilder.buildModelUid(modelName), value.get(GraphPropertyEnum.UNIQUE_ID));
+    }
+
+    @Test
+    void findModelVertexTest_modelNotFound() {
+        final ArgumentCaptor<Map<GraphPropertyEnum, Object>> mapArgumentCaptor = ArgumentCaptor.forClass(Map.class);
+        when(janusGraphDao.getByCriteria(eq(VertexTypeEnum.MODEL), mapArgumentCaptor.capture()))
+            .thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
+        var modelName = "modelName";
+
+        final Optional<GraphVertex> modelVertexByNameOpt = modelOperation.findModelVertexByName(modelName);
+
+        assertTrue(modelVertexByNameOpt.isEmpty());
+        final Map<GraphPropertyEnum, Object> value = mapArgumentCaptor.getValue();
+        assertEquals(modelName, value.get(GraphPropertyEnum.NAME));
+        assertEquals(UniqueIdBuilder.buildModelUid(modelName), value.get(GraphPropertyEnum.UNIQUE_ID));
+    }
+
+    @Test
+    void findModelVertexTest_janusGraphError() {
+        final ArgumentCaptor<Map<GraphPropertyEnum, Object>> 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<GraphPropertyEnum, Object> 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<Map<GraphPropertyEnum, Object>> 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<Model> modelByNameOpt = modelOperation.findModelByName(modelName);
+
+        final Map<GraphPropertyEnum, Object> 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<Map<GraphPropertyEnum, Object>> mapArgumentCaptor = ArgumentCaptor.forClass(Map.class);
+        var modelName = "modelName";
+        when(janusGraphDao.getByCriteria(eq(VertexTypeEnum.MODEL), mapArgumentCaptor.capture()))
+            .thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND));
+        final Optional<Model> modelByNameOpt = modelOperation.findModelByName(modelName);
+        assertTrue(modelByNameOpt.isEmpty());
+    }
+
+    @Test
+    void findModelByNameTest_emptyOrNullModelName() {
+        assertTrue(modelOperation.findModelByName("").isEmpty());
+        assertTrue(modelOperation.findModelByName(null).isEmpty());
     }
 
 }
diff --git a/common-be/src/main/java/org/openecomp/sdc/be/data/model/ToscaImportByModel.java b/common-be/src/main/java/org/openecomp/sdc/be/data/model/ToscaImportByModel.java
new file mode 100644 (file)
index 0000000..5e8818b
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * ============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.data.model;
+
+import com.datastax.driver.mapping.annotations.Column;
+import com.datastax.driver.mapping.annotations.PartitionKey;
+import com.datastax.driver.mapping.annotations.Table;
+import lombok.Data;
+
+@Data
+@Table(keyspace = "sdcartifact", name = "tosca_import_by_model")
+public class ToscaImportByModel {
+
+    @PartitionKey
+    @Column(name = "model_id")
+    private String modelId;
+    @PartitionKey(1)
+    @Column(name = "full_path")
+    private String fullPath;
+    @Column(name = "content")
+    private String content;
+
+}
index 144822d..ad38d6f 100644 (file)
@@ -21,8 +21,12 @@ package org.onap.sdc.tosca.datatypes.model;
 
 import java.util.List;
 import java.util.Map;
+import lombok.Getter;
+import lombok.Setter;
 import org.onap.sdc.tosca.services.DataModelCloneUtil;
 
+@Getter
+@Setter
 public class CapabilityDefinition implements Cloneable {
 
     private String type;
@@ -32,63 +36,14 @@ public class CapabilityDefinition implements Cloneable {
     private List<String> valid_source_types;
     private Object[] occurrences;
 
-    /**
-     * Constructor.
-     */
     public CapabilityDefinition() {
-        occurrences = new Object[2];
-        occurrences[0] = 1;
-        occurrences[1] = "UNBOUNDED";
+        occurrences = new Object[] {1, "UNBOUNDED"};
     }
 
-    public String getType() {
-        return type;
-    }
-
-    public void setType(String type) {
+    public CapabilityDefinition(final String type) {
         this.type = type;
     }
 
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public Map<String, PropertyDefinition> getProperties() {
-        return properties;
-    }
-
-    public void setProperties(Map<String, PropertyDefinition> properties) {
-        this.properties = properties;
-    }
-
-    public Map<String, AttributeDefinition> getAttributes() {
-        return attributes;
-    }
-
-    public void setAttributes(Map<String, AttributeDefinition> attributes) {
-        this.attributes = attributes;
-    }
-
-    public List<String> getValid_source_types() {
-        return valid_source_types;
-    }
-
-    public void setValid_source_types(List<String> valid_source_types) {
-        this.valid_source_types = valid_source_types;
-    }
-
-    public Object[] getOccurrences() {
-        return occurrences;
-    }
-
-    public void setOccurrences(Object[] occurrences) {
-        this.occurrences = occurrences;
-    }
-
     @Override
     public CapabilityDefinition clone() {
         CapabilityDefinition capabilityDefinition = new CapabilityDefinition();
index 932e3c7..7e08dd8 100644 (file)
 package org.onap.sdc.tosca.datatypes.model;
 
 import java.util.List;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
 import org.onap.sdc.tosca.services.DataModelCloneUtil;
 
+@NoArgsConstructor
+@Getter
+@Setter
 public class EntrySchema implements Cloneable {
 
     private String description;
     private String type;
     private List<Constraint> constraints;
 
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public String getType() {
-        return type;
-    }
-
-    public void setType(String type) {
+    public EntrySchema(final String type) {
         this.type = type;
     }
 
-    public List<Constraint> getConstraints() {
-        return constraints;
-    }
-
-    public void setConstraints(List<Constraint> constraints) {
-        this.constraints = constraints;
-    }
-
     @Override
     public EntrySchema clone() {
         EntrySchema entrySchema = new EntrySchema();