From: aribeiro Date: Tue, 18 May 2021 15:31:28 +0000 (+0100) Subject: Add Support for creating model type X-Git-Tag: 1.9.1~112 X-Git-Url: https://gerrit.onap.org/r/gitweb?a=commitdiff_plain;h=b3d55d3dd8508919ae48d5f60425aa13e5731ac2;p=sdc.git Add Support for creating model type Add new vertex type called "model" was added to represent models Add new edge type called MODEL_ELEMENT was added to connect a tosca type to a model Add new edge type called MODEL was added to connect a resource/service to the model it is based on Issue-ID: SDC-3596 Signed-off-by: MichaelMorris Signed-off-by: aribeiro Change-Id: I310e14d0cf5a9ca0eb0bda592efe8a3baf73749c --- diff --git a/catalog-be/pom.xml b/catalog-be/pom.xml index c156b96178..d12e7c18cc 100644 --- a/catalog-be/pom.xml +++ b/catalog-be/pom.xml @@ -845,6 +845,13 @@ + + + com.googlecode.jmapper-framework + jmapper-core + ${jMapper.version} + + diff --git a/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml b/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml index b49c111c79..2c6a0c852b 100644 --- a/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml +++ b/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml @@ -2464,4 +2464,12 @@ errors: code: 500, message: "An unexpected error occurred while updating the capability '%1'.", messageId: "SVC4143" - } \ No newline at end of file + } + + #---------SVC4144------------------------------ + # %1 - "Model name" + MODEL_ALREADY_EXISTS: { + code: 409, + message: "Error: Model name '%1' already exists.", + messageId: "SVC4144" + } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogic.java new file mode 100644 index 0000000000..1ef4cef701 --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogic.java @@ -0,0 +1,43 @@ +/* + * ============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.impl; + +import org.openecomp.sdc.be.model.Model; +import org.openecomp.sdc.be.model.operations.impl.ModelOperation; +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 final ModelOperation modelOperation; + + @Autowired + public ModelBusinessLogic(final ModelOperation modelOperation) { + this.modelOperation = modelOperation; + } + + public Model createModel(final Model model) { + log.debug("createModel: creating model {}", model); + return modelOperation.createModel(model, false); + } +} \ No newline at end of file diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ModelServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ModelServlet.java new file mode 100644 index 0000000000..f4fc883b4c --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ModelServlet.java @@ -0,0 +1,122 @@ +/* + * ============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.servlets; + +import com.googlecode.jmapper.JMapper; +import com.jcabi.aspects.Loggable; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.ArraySchema; +import io.swagger.v3.oas.annotations.media.Content; +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.util.Arrays; +import javax.inject.Inject; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.ws.rs.Consumes; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +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.aaf.AafPermission; +import org.openecomp.sdc.be.components.impl.aaf.PermissionAllowed; +import org.openecomp.sdc.be.components.validation.UserValidations; +import org.openecomp.sdc.be.config.BeEcompErrorManager; +import org.openecomp.sdc.be.dao.api.ActionStatus; +import org.openecomp.sdc.be.exception.BusinessException; +import org.openecomp.sdc.be.impl.ComponentsUtils; +import org.openecomp.sdc.be.impl.ServletUtils; +import org.openecomp.sdc.be.model.Model; +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.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; + +/** + * Root resource (exposed at "/" path) + */ +@Loggable(prepend = true, value = Loggable.DEBUG, trim = false) +@Path("/v1/catalog") +@Tag(name = "SDCE-2 APIs") +@Server(url = "/sdc2/rest") +@Controller +public class ModelServlet extends AbstractValidationsServlet { + + private static final Logger log = LoggerFactory.getLogger(ModelServlet.class); + private final ModelBusinessLogic modelBusinessLogic; + private final UserValidations userValidations; + + @Inject + public ModelServlet(final UserBusinessLogic userBusinessLogic, final ComponentInstanceBusinessLogic componentInstanceBL, + final ComponentsUtils componentsUtils, final ServletUtils servletUtils, final ResourceImportManager resourceImportManager, + final ModelBusinessLogic modelBusinessLogic, final UserValidations userValidations) { + super(userBusinessLogic, componentInstanceBL, componentsUtils, servletUtils, resourceImportManager); + this.modelBusinessLogic = modelBusinessLogic; + this.userValidations = userValidations; + } + + @POST + @Path("/model") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(description = "Create model", method = "POST", summary = "Returns created 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) + 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); + try { + final Model modelCreateResponse = modelBusinessLogic + .createModel(new JMapper<>(Model.class, ModelCreateRequest.class).getDestination(modelCreateRequest)); + return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), + RepresentationUtils.toRepresentation(modelCreateResponse)); + } catch (final BusinessException e) { + throw e; + } catch (final Exception e) { + var errorMsg = String + .format("Unexpected error while creating model '%s'", modelCreateRequest.getName()); + BeEcompErrorManager.getInstance().logBeRestApiGeneralError(errorMsg); + log.error(errorMsg, e); + return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)); + } + } + + private void validateUser(final String userId) { + userValidations.validateUserRole(userValidations.validateUserExists(userId), Arrays.asList(Role.DESIGNER, Role.ADMIN)); + } + +} diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogicTest.java new file mode 100644 index 0000000000..6c7bd5323f --- /dev/null +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogicTest.java @@ -0,0 +1,80 @@ +/* + * ============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.impl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +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 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.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +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.operations.impl.ModelOperation; + +@TestInstance(Lifecycle.PER_CLASS) +@TestMethodOrder(OrderAnnotation.class) +class ModelBusinessLogicTest { + + @InjectMocks + private ModelBusinessLogic modelBusinessLogic; + @Mock + private ModelOperation modelOperation; + private Model model; + + @BeforeAll + void setup() { + MockitoAnnotations.openMocks(this); + initTestData(); + } + + private void initTestData() { + model = new Model("ETSI-SDC-MODEL-TEST"); + } + + @Test + @Order(1) + void createModelTest() { + when(modelOperation.createModel(model, false)).thenReturn(model); + final Model result = modelBusinessLogic.createModel(model); + assertThat(result).isNotNull(); + assertThat(result.getName()).isEqualTo(model.getName()); + } + + @Test + @Order(2) + void createModelFailTest() { + when(modelOperation.createModel(model, false)) + .thenThrow(new OperationException(ActionStatus.MODEL_ALREADY_EXISTS, model.getName())); + 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()); + } + +} \ No newline at end of file diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ModelServletTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ModelServletTest.java new file mode 100644 index 0000000000..34201d3c1f --- /dev/null +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ModelServletTest.java @@ -0,0 +1,208 @@ +/* + * ============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.servlets; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +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 org.apache.commons.lang3.StringUtils; +import org.eclipse.jetty.http.HttpStatus; +import org.glassfish.hk2.utilities.binding.AbstractBinder; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.glassfish.jersey.test.TestProperties; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +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.validation.UserValidations; +import org.openecomp.sdc.be.config.ConfigurationManager; +import org.openecomp.sdc.be.config.SpringConfig; +import org.openecomp.sdc.be.dao.api.ActionStatus; +import org.openecomp.sdc.be.exception.BusinessException; +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.ui.model.ModelCreateRequest; +import org.openecomp.sdc.be.user.UserBusinessLogic; +import org.openecomp.sdc.common.api.ConfigurationSource; +import org.openecomp.sdc.common.api.Constants; +import org.openecomp.sdc.common.impl.ExternalConfiguration; +import org.openecomp.sdc.common.impl.FSConfigurationSource; +import org.openecomp.sdc.exception.ResponseFormat; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.web.context.WebApplicationContext; + +@TestInstance(Lifecycle.PER_CLASS) +class ModelServletTest extends JerseyTest { + + private static final String USER_ID = "cs0008"; + + @Mock + private HttpServletRequest request; + @Mock + private HttpSession session; + @Mock + private ServletContext servletContext; + @Mock + private WebAppContextWrapper webAppContextWrapper; + @Mock + private WebApplicationContext webApplicationContext; + @Mock + private UserBusinessLogic userBusinessLogic; + @Mock + private ComponentInstanceBusinessLogic componentInstanceBusinessLogic; + @Mock + private ComponentsUtils componentsUtils; + @Mock + private ServletUtils servletUtils; + @Mock + private ResourceImportManager resourceImportManager; + @Mock + private ModelBusinessLogic modelBusinessLogic; + @InjectMocks + private ModelServlet modelServlet; + @Mock + private ResponseFormat responseFormat; + @Mock + private UserValidations userValidations; + + private Model model; + private Response response; + private ModelCreateRequest modelCreateRequest; + + @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)) + .thenReturn(webAppContextWrapper); + when(webAppContextWrapper.getWebAppContext(servletContext)).thenReturn(webApplicationContext); + when(webApplicationContext.getBean(ModelBusinessLogic.class)).thenReturn(modelBusinessLogic); + when(request.getHeader(Constants.USER_ID_HEADER)).thenReturn(USER_ID); + when(webApplicationContext.getBean(ServletUtils.class)).thenReturn(servletUtils); + when(servletUtils.getComponentsUtils()).thenReturn(componentsUtils); + final String appConfigDir = "src/test/resources/config/catalog-be"; + final ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir); + final ConfigurationManager configurationManager = new ConfigurationManager(configurationSource); + final org.openecomp.sdc.be.config.Configuration configuration = new org.openecomp.sdc.be.config.Configuration(); + configuration.setJanusGraphInMemoryGraph(true); + configurationManager.setConfiguration(configuration); + ExternalConfiguration.setAppName("catalog-be"); + } + + @BeforeEach + void resetMock() throws Exception { + super.setUp(); + initTestData(); + } + + @AfterEach + void after() throws Exception { + super.tearDown(); + } + + private void initTestData() { + final String modelName = "MY-INTEGRATION-TEST-MODEL"; + model = new Model(modelName); + modelCreateRequest = new ModelCreateRequest(); + modelCreateRequest.setName(modelName); + } + + @Override + protected ResourceConfig configure() { + forceSet(TestProperties.CONTAINER_PORT, "0"); + final ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); + return new ResourceConfig(ModelServlet.class) + .register(new AbstractBinder() { + @Override + protected void configure() { + bind(request).to(HttpServletRequest.class); + bind(userBusinessLogic).to(UserBusinessLogic.class); + bind(componentInstanceBusinessLogic).to(ComponentInstanceBusinessLogic.class); + bind(componentsUtils).to(ComponentsUtils.class); + bind(servletUtils).to(ServletUtils.class); + bind(resourceImportManager).to(ResourceImportManager.class); + bind(modelBusinessLogic).to(ModelBusinessLogic.class); + } + }) + .property("contextConfig", context); + } + + @Test + void createModelSuccessTest() { + 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); + } + + @Test + void createModelFailTest() { + 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); + } + + @Test + void createModelFailWithModelNameEmptyTest() { + 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"); + } + + @Test + void createModelFailWithModelNameNullTest() { + 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"); + } + + @Test + void createModelThrowsBusinessExceptionTest() { + when(modelBusinessLogic.createModel(model)).thenThrow(new BusinessException() {}); + assertThrows(BusinessException.class, () -> modelServlet.createModel(modelCreateRequest, USER_ID)); + } + +} \ No newline at end of file diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java index 619f88263c..1ebaeab54f 100644 --- a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java +++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/api/ActionStatus.java @@ -51,6 +51,8 @@ public enum ActionStatus { VENDOR_NAME_EXCEEDS_LIMIT, VENDOR_RELEASE_EXCEEDS_LIMIT, INVALID_VENDOR_NAME, INVALID_VENDOR_RELEASE, MISSING_VENDOR_NAME, MISSING_VENDOR_RELEASE, RESOURCE_VENDOR_MODEL_NUMBER_EXCEEDS_LIMIT, INVALID_RESOURCE_VENDOR_MODEL_NUMBER, // 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, // Service API URL INVALID_SERVICE_API_URL, // Property related diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/GraphElementFactory.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/GraphElementFactory.java index c3c5e9f704..1c500e722d 100644 --- a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/GraphElementFactory.java +++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/graph/GraphElementFactory.java @@ -46,6 +46,7 @@ import org.openecomp.sdc.be.resources.data.HeatParameterValueData; import org.openecomp.sdc.be.resources.data.InputValueData; import org.openecomp.sdc.be.resources.data.InputsData; import org.openecomp.sdc.be.resources.data.InterfaceData; +import org.openecomp.sdc.be.resources.data.ModelData; import org.openecomp.sdc.be.resources.data.OperationData; import org.openecomp.sdc.be.resources.data.PolicyTypeData; import org.openecomp.sdc.be.resources.data.ProductMetadataData; @@ -255,6 +256,10 @@ public class GraphElementFactory { break; case AnnotationType: element = clazz.cast(new AnnotationTypeData(properties)); + break; + case Model: + element = clazz.cast(new ModelData(properties)); + break; default: break; } diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/jsongraph/types/EdgeLabelEnum.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/jsongraph/types/EdgeLabelEnum.java index 33a4391c9f..7fc954e0a8 100644 --- a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/jsongraph/types/EdgeLabelEnum.java +++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/jsongraph/types/EdgeLabelEnum.java @@ -69,7 +69,8 @@ public enum EdgeLabelEnum { INST_INTERFACES, NODE_FILTER_TEMPLATE, SUBSTITUTION_FILTER_TEMPLATE, - DATA_TYPES; + DATA_TYPES, + MODEL_ELEMENT; // @formatter:on /** diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/jsongraph/types/VertexTypeEnum.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/jsongraph/types/VertexTypeEnum.java index cc8c8bcd87..e9648a3579 100644 --- a/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/jsongraph/types/VertexTypeEnum.java +++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/jsongraph/types/VertexTypeEnum.java @@ -92,7 +92,8 @@ public enum VertexTypeEnum { NODE_FILTER_TEMPLATE ("NodeTemplateFilter", CINodeFilterDataDefinition.class), SUBSTITUTION_FILTER_TEMPLATE ("substitution_mapping", SubstitutionFilterDataDefinition.class), INST_INTERFACES ("InstInterfaces", MapInterfaceDataDefinition.class), - DATA_TYPES ("data_types", DataTypeDataDefinition.class); + DATA_TYPES ("data_types", DataTypeDataDefinition.class), + MODEL ("model", null); // @formatter:on private final String name; diff --git a/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ModelData.java b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ModelData.java new file mode 100644 index 0000000000..026b1d7998 --- /dev/null +++ b/catalog-dao/src/main/java/org/openecomp/sdc/be/resources/data/ModelData.java @@ -0,0 +1,54 @@ +/* + * ============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.resources.data; + +import java.util.HashMap; +import java.util.Map; +import lombok.Getter; +import org.openecomp.sdc.be.dao.graph.datatype.GraphNode; +import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary; +import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum; + +@Getter +public class ModelData extends GraphNode { + + private final String name; + private final String uniqueId; + + public ModelData(final String name, final String uniqueId) { + super(NodeTypeEnum.Model); + this.name = name; + this.uniqueId = uniqueId; + } + + public ModelData(final Map properties) { + super(NodeTypeEnum.Model); + name = (String) properties.get(GraphPropertiesDictionary.NAME.getProperty()); + uniqueId = (String) properties.get(GraphPropertiesDictionary.UNIQUE_ID.getProperty()); + } + + @Override + public Map toGraphMap() { + final Map map = new HashMap<>(); + addIfExists(map, GraphPropertiesDictionary.NAME, name); + addIfExists(map, GraphPropertiesDictionary.UNIQUE_ID, uniqueId); + return map; + } + +} \ No newline at end of file diff --git a/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/data/ModelDataTest.java b/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/data/ModelDataTest.java new file mode 100644 index 0000000000..1fb34dd5e6 --- /dev/null +++ b/catalog-dao/src/test/java/org/openecomp/sdc/be/resources/data/ModelDataTest.java @@ -0,0 +1,70 @@ +/* + * ============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.resources.data; + +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; + +@TestInstance(Lifecycle.PER_CLASS) +class ModelDataTest { + + private final String modelName = "ETSI-SDC-MODEL-TEST"; + private final String modelId = UUID.randomUUID().toString(); + ModelData modelData; + + @BeforeAll + void initTestData() { + modelData = new ModelData(modelName, modelId); + } + + @Test + void modelDataTest() { + assertThat(modelData).isNotNull(); + assertThat(modelData.getUniqueId()).isEqualTo(modelId); + assertThat(modelData.getName()).isEqualTo(modelName); + } + + @Test + void modelDataFromPropertiesMapTest() { + final Map properties = new HashMap(); + properties.put("name", modelData.getName()); + properties.put("uid", modelData.getUniqueId()); + final ModelData modelDataFromPropertiesMap = new ModelData(properties); + assertThat(modelDataFromPropertiesMap).isNotNull(); + assertThat(modelDataFromPropertiesMap.getUniqueId()).isEqualTo(modelId); + assertThat(modelDataFromPropertiesMap.getName()).isEqualTo(modelName); + } + + @Test + void modelDataToGraphTest() { + final var result = modelData.toGraphMap(); + assertThat(result).isNotNull(); + assertThat(result).isNotEmpty(); + assertThat(result.values()).contains(modelId); + assertThat(result.values()).contains(modelName); + } + +} diff --git a/catalog-model/pom.xml b/catalog-model/pom.xml index ff28ea6395..fc55fb771c 100644 --- a/catalog-model/pom.xml +++ b/catalog-model/pom.xml @@ -364,6 +364,12 @@ 3.1.0 + + com.googlecode.jmapper-framework + jmapper-core + ${jMapper.version} + + diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/Model.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Model.java new file mode 100644 index 0000000000..99d0f6599e --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/Model.java @@ -0,0 +1,34 @@ +/* + * ============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; + +import com.googlecode.jmapper.annotations.JGlobalMap; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@AllArgsConstructor +@NoArgsConstructor +@JGlobalMap +public class Model { + + private String name; + +} diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java new file mode 100644 index 0000000000..c604df6dde --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/ModelOperation.java @@ -0,0 +1,76 @@ +/* + * ============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.operations.impl; + +import fj.data.Either; +import java.util.Objects; +import org.openecomp.sdc.be.dao.api.ActionStatus; +import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao; +import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus; +import org.openecomp.sdc.be.model.Model; +import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; +import org.openecomp.sdc.be.resources.data.ModelData; +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("model-operation") +public class ModelOperation extends AbstractOperation { + + private static final Logger log = Logger.getLogger(ModelOperation.class); + + private final JanusGraphGenericDao genericDao; + + @Autowired + public ModelOperation(final JanusGraphGenericDao janusGraphGenericDao) { + this.genericDao = janusGraphGenericDao; + } + + public Model createModel(final Model model, final boolean inTransaction) { + Model result = null; + final ModelData modelData = new ModelData(model.getName(), UniqueIdBuilder.buildModelUid(model.getName())); + try { + final Either createNode = genericDao.createNode(modelData, ModelData.class); + if (createNode.isRight()) { + final JanusGraphOperationStatus 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 new OperationException(ActionStatus.GENERAL_ERROR, + String.format("Failed to create model %s on JanusGraph with %s error", model, janusGraphOperationStatus)); + } + result = new Model(createNode.left().value().getName()); + return result; + } finally { + if (!inTransaction) { + if (Objects.nonNull(result)) { + genericDao.commit(); + } else { + genericDao.rollback(); + } + } + } + } + +} + + diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/UniqueIdBuilder.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/UniqueIdBuilder.java index c9225c7295..62404aa409 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/UniqueIdBuilder.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/operations/impl/UniqueIdBuilder.java @@ -202,4 +202,8 @@ public class UniqueIdBuilder { public static String buildGroupPropertyValueUid(String groupUniqueId, Integer index) { return groupUniqueId + DOT + "property" + DOT + index; } + + public static String buildModelUid(final String modelName) { + return NodeTypeEnum.Model.getName() + DOT + modelName; + } } diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/ModelCreateRequest.java b/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/ModelCreateRequest.java new file mode 100644 index 0000000000..0d294b42c4 --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/ModelCreateRequest.java @@ -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.ui.model; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import lombok.Data; + +/** + * This class is responsible for holding all required fields from the create Model post request. + * It also validates the Model 'name' field. + */ +@Data +public class ModelCreateRequest { + + @NotNull(message = "Model name cannot be null") + @Size(min = 2, message = "Model name cannot be empty") + private String name; + +} diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java new file mode 100644 index 0000000000..c1c0132b2c --- /dev/null +++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/operations/impl/ModelOperationTest.java @@ -0,0 +1,80 @@ +/* + * ============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.operations.impl; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.when; + +import fj.data.Either; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao; +import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus; +import org.openecomp.sdc.be.model.Model; +import org.openecomp.sdc.be.model.ModelTestBase; +import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; +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; + + private final String modelName = "ETSI-SDC-MODEL-TEST"; + + @BeforeAll + void setup() { + init(); + MockitoAnnotations.openMocks(this); + } + + @Test + void createModelSuccessTest() { + final ModelData modelData = new ModelData(modelName, UniqueIdBuilder.buildModelUid(modelName)); + when(janusGraphGenericDao.createNode(any(),any())).thenReturn(Either.left(modelData)); + final Model createdModel = modelOperation.createModel(new Model(modelName), false); + assertThat(createdModel).isNotNull(); + assertThat(createdModel.getName()).isEqualTo(modelName); + } + + @Test + void createModelFailWithModelAlreadyExistTest() { + when(janusGraphGenericDao.createNode(any(),any())).thenReturn(Either.right(JanusGraphOperationStatus.JANUSGRAPH_SCHEMA_VIOLATION)); + assertThrows(OperationException.class, () -> modelOperation.createModel(new Model(modelName), 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)); + } + +} diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/JsonPresentationFields.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/JsonPresentationFields.java index 4cdf0508c7..185d9538ff 100644 --- a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/JsonPresentationFields.java +++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/JsonPresentationFields.java @@ -68,7 +68,7 @@ public enum JsonPresentationFields { DERIVED_FROM_GENERIC_TYPE("derivedFromGenericType", null), DERIVED_FROM_GENERIC_VERSION("derivedFromGenericVersion", null), SERVICE_FUNCTION("serviceFunction", null), - + MODELS("models", null), DATA_TYPES("data_types", GraphPropertyEnum.DATA_TYPES), ////Inputs diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/NodeTypeEnum.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/NodeTypeEnum.java index 75a2177250..2a927b8f4a 100644 --- a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/NodeTypeEnum.java +++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/enums/NodeTypeEnum.java @@ -63,7 +63,8 @@ public enum NodeTypeEnum { Input("input"), GroupInstance("groupInstance"), AnnotationType("annotationType"), - Component("component"); + Component("component"), + Model("model"); // @formatter:on private final String name; diff --git a/pom.xml b/pom.xml index 5427d1de4f..341b29d3a2 100644 --- a/pom.xml +++ b/pom.xml @@ -133,6 +133,7 @@ Modifications copyright (c) 2018-2019 Nokia 1.3 2.22.2 2.4 + 1.6.0.1 ${project.version} @@ -735,6 +736,8 @@ Modifications copyright (c) 2018-2019 Nokia true true true + + true