From 2152a9a43767cdd486fd8c93894f66a05083f53c Mon Sep 17 00:00:00 2001 From: "andre.schmid" Date: Wed, 5 May 2021 15:31:04 +0100 Subject: Support for selection of capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Ib1a3e3e1a59fc84c62620932c408e231acf77024 Issue-ID: SDC-3580 Signed-off-by: André Schmid --- .../files/default/error-configuration.yaml | 28 +++ .../impl/ComponentInstanceBusinessLogic.java | 69 ++++++ .../impl/exceptions/ComponentException.java | 3 +- .../ComponentInstanceCapabilityServlet.java | 144 +++++++++++++ .../sdc/be/servlets/ComponentInstanceServlet.java | 2 +- .../servlets/builder/ServletResponseBuilder.java | 72 +++++++ .../exception/OperationExceptionMapper.java | 50 +++++ .../be/tosca/CapabilityRequirementConverter.java | 8 +- .../sdc/config/CatalogBESpringConfig.java | 3 +- .../impl/ComponentInstanceBusinessLogicTest.java | 217 ++++++++++++++++++- .../ComponentInstanceCapabilityServletTest.java | 240 +++++++++++++++++++++ .../sdc/be/servlets/JerseySpringBaseTest.java | 1 + .../tosca/CapabilityRequirementConverterTest.java | 1 + .../config/catalog-be/error-configuration.yaml | 28 +++ 14 files changed, 858 insertions(+), 8 deletions(-) create mode 100644 catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInstanceCapabilityServlet.java create mode 100644 catalog-be/src/main/java/org/openecomp/sdc/be/servlets/builder/ServletResponseBuilder.java create mode 100644 catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/OperationExceptionMapper.java create mode 100644 catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ComponentInstanceCapabilityServletTest.java (limited to 'catalog-be/src') 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 3e1b16d82f..b49c111c79 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 @@ -2437,3 +2437,31 @@ errors: message: '%1 is not yet supported', messageId: "SVC4139" } + #---------SVC4140------------------------------ + # %1 - Component uid + COMPONENT_FIND_ERROR: { + code: 500, + message: "An unexpected error occurred while retrieving the component '%1'.", + messageId: "SVC4140" + } + #---------SVC4141------------------------------ + # %1 - Component uid + COMPONENT_CAPABILITIES_FIND_ERROR: { + code: 500, + message: "An unexpected error occurred while retrieving the component '%1' capabilities.", + messageId: "SVC4141" + } + #---------SVC4142------------------------------ + # %1 - Component uid or name + COMPONENT_NOT_FOUND: { + code: 404, + message: "Component '%1' was not found.", + messageId: "SVC4142" + } + #---------SVC4143------------------------------ + # %1 - Capability name + COMPONENT_INSTANCE_CAPABILITY_UPDATE_ERROR: { + code: 500, + message: "An unexpected error occurred while updating the capability '%1'.", + messageId: "SVC4143" + } \ No newline at end of file diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java index b602072354..b8fabc307f 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogic.java @@ -74,6 +74,7 @@ import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum; import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum; import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum; import org.openecomp.sdc.be.datatypes.tosca.ToscaGetFunctionType; +import org.openecomp.sdc.be.exception.BusinessException; import org.openecomp.sdc.be.impl.ComponentsUtils; import org.openecomp.sdc.be.impl.ForwardingPathUtils; import org.openecomp.sdc.be.impl.ServiceFilterUtils; @@ -108,6 +109,7 @@ import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ArtifactsOperations; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ForwardingPathOperation; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.InterfaceOperation; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.NodeFilterOperation; +import org.openecomp.sdc.be.model.jsonjanusgraph.operations.NodeTemplateOperation; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade; import org.openecomp.sdc.be.model.jsonjanusgraph.utils.ModelConverter; import org.openecomp.sdc.be.model.operations.StorageException; @@ -130,6 +132,8 @@ import org.openecomp.sdc.common.api.Constants; import org.openecomp.sdc.common.datastructure.Wrapper; import org.openecomp.sdc.common.jsongraph.util.CommonUtility; import org.openecomp.sdc.common.jsongraph.util.CommonUtility.LogLevelEnum; +import org.openecomp.sdc.common.log.elements.ErrorLogOptionalData; +import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode; import org.openecomp.sdc.common.log.wrappers.Logger; import org.openecomp.sdc.common.util.ValidationUtils; import org.openecomp.sdc.exception.ResponseFormat; @@ -153,6 +157,8 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic { private static final String FAILED_TO_COPY_COMP_INSTANCE_TO_CANVAS = "Failed to copy the component instance to the canvas"; private static final String COPY_COMPONENT_INSTANCE_OK = "Copy component instance OK"; private static final String CANNOT_ATTACH_RESOURCE_INSTANCES_TO_CONTAINER_RESOURCE_OF_TYPE = "Cannot attach resource instances to container resource of type {}"; + private static final String FAILED_TO_UPDATE_COMPONENT_INSTANCE_CAPABILITY = "Failed to update component instance capability on instance {} in " + + "container {}"; private static final String SERVICE_PROXY = "serviceProxy"; private static final String ASSOCIATE_RI_TO_RI = "associateRIToRI"; private ComponentInstanceOperation componentInstanceOperation; @@ -3238,6 +3244,69 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic { } } + public Either updateInstanceCapability(final ComponentTypeEnum containerComponentType, + final String containerComponentId, + final String componentInstanceUniqueId, + final CapabilityDefinition capabilityDefinition, + final String userId) { + if (containerComponentType == null) { + BeEcompErrorManager.getInstance().logInvalidInputError("updateInstanceCapability", INVALID_COMPONENT_TYPE, ErrorSeverity.INFO); + return Either.right(componentsUtils.getResponseFormat(ActionStatus.NOT_ALLOWED)); + } + validateUserExists(userId); + final Either getResourceResult = toscaOperationFacade.getToscaFullElement(containerComponentId); + if (getResourceResult.isRight()) { + log.debug(FAILED_TO_RETRIEVE_COMPONENT_COMPONENT_ID, containerComponentId); + return Either.right(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_NOT_FOUND, containerComponentId)); + } + final Component containerComponent = getResourceResult.left().value(); + if (!ComponentValidationUtils.canWorkOnComponent(containerComponent, userId)) { + log.info("Restricted operation for user: {} on component {}", userId, containerComponentId); + return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION)); + } + final Either resourceInstanceStatus = + getResourceInstanceById(containerComponent, componentInstanceUniqueId); + if (resourceInstanceStatus.isRight()) { + return Either.right(componentsUtils + .getResponseFormat(ActionStatus.RESOURCE_INSTANCE_NOT_FOUND_ON_SERVICE, componentInstanceUniqueId, containerComponentId)); + } + // lock resource + final StorageOperationStatus lockStatus = graphLockOperation.lockComponent(containerComponentId, containerComponentType.getNodeType()); + if (lockStatus != StorageOperationStatus.OK) { + log.debug("Failed to lock component {}", containerComponentId); + return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(lockStatus))); + } + var success = false; + try { + final CapabilityDataDefinition updatedCapabilityDefinition = toscaOperationFacade + .updateComponentInstanceCapability(containerComponentId, componentInstanceUniqueId, capabilityDefinition); + final Either updateContainerEither = toscaOperationFacade + .updateComponentInstanceMetadataOfTopologyTemplate(containerComponent); + if (updateContainerEither.isRight()) { + var actionStatus = componentsUtils.convertFromStorageResponse(updateContainerEither.right().value(), containerComponentType); + return Either.right(componentsUtils.getResponseFormat(actionStatus)); + } + success = true; + return Either.left(new CapabilityDefinition(updatedCapabilityDefinition)); + } catch (final BusinessException e) { + log.error(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR, NodeTemplateOperation.class.getName(), (ErrorLogOptionalData) null, + FAILED_TO_UPDATE_COMPONENT_INSTANCE_CAPABILITY, componentInstanceUniqueId, containerComponentId); + throw e; + } catch (final Exception e) { + log.error(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR, NodeTemplateOperation.class.getName(), (ErrorLogOptionalData) null, + FAILED_TO_UPDATE_COMPONENT_INSTANCE_CAPABILITY, componentInstanceUniqueId, containerComponentId); + throw new ByResponseFormatComponentException(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR)); + } finally { + if (success) { + janusGraphDao.commit(); + } else { + janusGraphDao.rollback(); + } + // unlock resource + graphLockOperation.unlockComponent(containerComponentId, containerComponentType.getNodeType()); + } + } + public Either, ResponseFormat> updateInstanceCapabilityProperties(ComponentTypeEnum componentTypeEnum, String containerComponentId, String componentInstanceUniqueId, diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/exceptions/ComponentException.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/exceptions/ComponentException.java index 351d279875..7410e888f0 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/exceptions/ComponentException.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/exceptions/ComponentException.java @@ -22,10 +22,11 @@ package org.openecomp.sdc.be.components.impl.exceptions; import javax.annotation.Nullable; import org.openecomp.sdc.be.components.impl.ResponseFormatManager; import org.openecomp.sdc.be.dao.api.ActionStatus; +import org.openecomp.sdc.be.exception.BusinessException; import org.openecomp.sdc.be.model.Resource; import org.openecomp.sdc.exception.ResponseFormat; -public class ComponentException extends RuntimeException { +public class ComponentException extends BusinessException { /** * This class will be initialized either by action status and params or by ResponseFormat diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInstanceCapabilityServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInstanceCapabilityServlet.java new file mode 100644 index 0000000000..f83f83df2b --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInstanceCapabilityServlet.java @@ -0,0 +1,144 @@ +/* + * ============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.jcabi.aspects.Loggable; +import fj.data.Either; +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 javax.servlet.http.HttpServletRequest; +import javax.validation.Valid; +import javax.validation.constraints.NotNull; +import javax.ws.rs.Consumes; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.PUT; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.Context; +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.ResponseFormatManager; +import org.openecomp.sdc.be.components.impl.aaf.AafPermission; +import org.openecomp.sdc.be.components.impl.aaf.PermissionAllowed; +import org.openecomp.sdc.be.config.BeEcompErrorManager; +import org.openecomp.sdc.be.dao.api.ActionStatus; +import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum; +import org.openecomp.sdc.be.exception.BusinessException; +import org.openecomp.sdc.be.model.CapabilityDefinition; +import org.openecomp.sdc.be.servlets.builder.ServletResponseBuilder; +import org.openecomp.sdc.be.ui.mapper.CapabilityMapper; +import org.openecomp.sdc.be.ui.model.ComponentInstanceCapabilityUpdateModel; +import org.openecomp.sdc.common.api.Constants; +import org.openecomp.sdc.common.log.elements.LoggerSupportability; +import org.openecomp.sdc.common.log.enums.LoggerSupportabilityActions; +import org.openecomp.sdc.common.log.enums.StatusCode; +import org.openecomp.sdc.common.log.wrappers.Logger; +import org.openecomp.sdc.exception.ResponseFormat; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestBody; + +/** + * Handles component instance capabilities operations + */ +@Loggable(prepend = true, value = Loggable.DEBUG, trim = false) +@Path("/v1/catalog") +@Tag(name = "SDCE-2 APIs") +@Server(url = "/sdc2/rest") +@Controller +public class ComponentInstanceCapabilityServlet { + + private static final Logger LOGGER = Logger.getLogger(ComponentInstanceCapabilityServlet.class); + private static final LoggerSupportability LOGGER_SUPPORTABILITY = LoggerSupportability.getLogger(ComponentInstanceCapabilityServlet.class); + + private final ResponseFormatManager responseFormatManager; + private final ComponentInstanceBusinessLogic componentInstanceBusinessLogic; + private final CapabilityMapper capabilityMapper; + private final ServletResponseBuilder servletResponseBuilder; + + public ComponentInstanceCapabilityServlet(final ComponentInstanceBusinessLogic componentInstanceBusinessLogic, + final CapabilityMapper capabilityMapper, final ServletResponseBuilder servletResponseBuilder) { + this.capabilityMapper = capabilityMapper; + this.servletResponseBuilder = servletResponseBuilder; + this.responseFormatManager = ResponseFormatManager.getInstance(); + this.componentInstanceBusinessLogic = componentInstanceBusinessLogic; + } + + @PUT + @Path("/{containerComponentType}/{containerComponentId}/componentInstances/{componentInstanceUniqueId}/capability/") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(description = "Update Component Instance Capability", method = "PUT", summary = "Returns updated Component Instance Capability", + responses = { + @ApiResponse(content = @Content(array = @ArraySchema(schema = @Schema(implementation = Response.class)))), + @ApiResponse(responseCode = "200", description = "Resource instance capability successfully updated"), + @ApiResponse(responseCode = "403", description = "Restricted operation"), + @ApiResponse(responseCode = "400", description = "Invalid content / Missing content"), + @ApiResponse(responseCode = "404", description = "Component/Component Instance/Capability not found")}) + @PermissionAllowed(AafPermission.PermNames.INTERNAL_ALL_VALUE) + public Response updateInstanceRequirement(@PathParam("containerComponentType") final String containerComponentType, + @PathParam("containerComponentId") final String containerComponentId, + @PathParam("componentInstanceUniqueId") final String componentInstanceUniqueId, + @Parameter(description = "Component instance capability to update", required = true) + @Valid @RequestBody @NotNull final ComponentInstanceCapabilityUpdateModel capabilityUpdateModel, + @Context final HttpServletRequest request, + @HeaderParam(value = Constants.USER_ID_HEADER) final String userId) { + if (LOGGER.isDebugEnabled()) { + final var url = request.getMethod() + " " + request.getRequestURI(); + LOGGER.debug("Start handle request of {}", url); + } + try { + var componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType); + if (componentTypeEnum == null) { + LOGGER.debug("Unsupported component type {}", containerComponentType); + return servletResponseBuilder + .buildErrorResponse(responseFormatManager.getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, containerComponentType)); + } + final var capabilityDefinition = capabilityMapper.mapToCapabilityDefinition(capabilityUpdateModel); + LOGGER_SUPPORTABILITY.log(LoggerSupportabilityActions.UPDATE_INSTANCE_CAPABILITY, StatusCode.STARTED, + "Starting to update capability '{}' in component instance '{}' by '{}'", + capabilityDefinition.getName(), componentInstanceUniqueId, userId); + final Either response = componentInstanceBusinessLogic + .updateInstanceCapability(componentTypeEnum, containerComponentId, componentInstanceUniqueId, capabilityDefinition, userId); + if (response.isRight()) { + return servletResponseBuilder.buildErrorResponse(response.right().value()); + } + return servletResponseBuilder.buildOkResponse(responseFormatManager.getResponseFormat(ActionStatus.OK), response.left().value()); + } catch (final BusinessException e) { + //leave to the handlers deal with it + throw e; + } catch (final Exception e) { + var errorMsg = String + .format("Unexpected error while updating component '%s' of type '%s' instance '%s'. Payload '%s'", + containerComponentId, containerComponentType, componentInstanceUniqueId, capabilityUpdateModel); + BeEcompErrorManager.getInstance().logBeRestApiGeneralError(errorMsg); + LOGGER.debug(errorMsg, e); + return servletResponseBuilder.buildErrorResponse(responseFormatManager.getResponseFormat(ActionStatus.GENERAL_ERROR)); + } + } + +} diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInstanceServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInstanceServlet.java index 7816d3d0be..d44c26ee6a 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInstanceServlet.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ComponentInstanceServlet.java @@ -1046,7 +1046,7 @@ public class ComponentInstanceServlet extends AbstractValidationsServlet { try { log.debug(START_HANDLE_REQUEST_OF, url); ComponentTypeEnum componentTypeEnum = ComponentTypeEnum.findByParamName(containerComponentType); - if (componentInstanceBusinessLogic == null) { + if (componentTypeEnum == null) { log.debug(UNSUPPORTED_COMPONENT_TYPE, containerComponentType); return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.UNSUPPORTED_ERROR, containerComponentType)); } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/builder/ServletResponseBuilder.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/builder/ServletResponseBuilder.java new file mode 100644 index 0000000000..a36ccf5133 --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/builder/ServletResponseBuilder.java @@ -0,0 +1,72 @@ +/* + * ============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.builder; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import java.util.Map; +import java.util.Map.Entry; +import javax.ws.rs.core.Response; +import org.apache.commons.collections.MapUtils; +import org.openecomp.sdc.common.log.wrappers.Logger; +import org.openecomp.sdc.exception.ResponseFormat; +import org.springframework.stereotype.Service; + +@Service +public class ServletResponseBuilder { + + private final Gson gson; + + public ServletResponseBuilder() { + gson = new GsonBuilder().setPrettyPrinting().create(); + } + + private static final Logger log = Logger.getLogger(ServletResponseBuilder.class); + + public Response buildErrorResponse(final ResponseFormat requestErrorWrapper) { + return Response.status(requestErrorWrapper.getStatus()).entity(gson.toJson(requestErrorWrapper.getRequestError())).build(); + } + + public Response buildOkResponse(final ResponseFormat errorResponseWrapper, final Object entity) { + return buildOkResponse(errorResponseWrapper, entity, null); + } + + public Response buildOkResponse(final ResponseFormat errorResponseWrapper, final Object entity, + final Map additionalHeaders) { + final int status = errorResponseWrapper.getStatus(); + var responseBuilder = Response.status(status); + if (entity != null) { + if (log.isTraceEnabled()) { + log.trace("returned entity is {}", entity.toString()); + } + responseBuilder = responseBuilder.entity(entity); + } + if (MapUtils.isNotEmpty(additionalHeaders)) { + for (final Entry additionalHeader : additionalHeaders.entrySet()) { + final String headerName = additionalHeader.getKey(); + final String headerValue = additionalHeader.getValue(); + log.trace("Adding header {} with value {} to the response", headerName, headerValue); + responseBuilder = responseBuilder.header(headerName, headerValue); + } + } + return responseBuilder.build(); + } + +} diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/OperationExceptionMapper.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/OperationExceptionMapper.java new file mode 100644 index 0000000000..7c25f8aef8 --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/exception/OperationExceptionMapper.java @@ -0,0 +1,50 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.servlets.exception; + +import javax.ws.rs.core.Response; +import javax.ws.rs.ext.ExceptionMapper; +import javax.ws.rs.ext.Provider; +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.stereotype.Component; + +@Component +@Provider +public class OperationExceptionMapper implements ExceptionMapper { + + private final ServletResponseBuilder servletResponseBuilder; + private final ResponseFormatManager responseFormatManager; + + private static final Logger log = Logger.getLogger(OperationExceptionMapper.class); + + public OperationExceptionMapper(final ServletResponseBuilder servletResponseBuilder) { + this.servletResponseBuilder = servletResponseBuilder; + this.responseFormatManager = ResponseFormatManager.getInstance(); + } + + @Override + public Response toResponse(final OperationException exception) { + log.debug("Handling OperationException response", exception); + return servletResponseBuilder.buildErrorResponse(responseFormatManager.getResponseFormat(exception.getActionStatus(), exception.getParams())); + } +} diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CapabilityRequirementConverter.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CapabilityRequirementConverter.java index ac4f8bf6c8..88f3666305 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CapabilityRequirementConverter.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/CapabilityRequirementConverter.java @@ -323,9 +323,11 @@ public class CapabilityRequirementConverter { Map toscaCapabilities = new HashMap<>(); Either, ToscaError> result = null; for (Map.Entry> entry : capabilities.entrySet()) { - Optional failedToAddRequirement = entry.getValue().stream().filter( - c -> !addEntry(componentsCache, toscaCapabilities, component, new SubstitutionEntry(c.getName(), c.getParentName(), ""), - c.getPreviousName(), c.getOwnerId(), c.getPath())).findAny(); + Optional failedToAddRequirement = entry.getValue().stream() + .filter(CapabilityDataDefinition::isExternal) + .filter( c -> !addEntry(componentsCache, toscaCapabilities, component, new SubstitutionEntry(c.getName(), c.getParentName(), "") + , c.getPreviousName(), c.getOwnerId(), c.getPath())) + .findAny(); if (failedToAddRequirement.isPresent()) { logger.debug("Failed to convert capability {} for substitution mappings section of a tosca template of the component {}. ", failedToAddRequirement.get().getName(), component.getName()); diff --git a/catalog-be/src/main/java/org/openecomp/sdc/config/CatalogBESpringConfig.java b/catalog-be/src/main/java/org/openecomp/sdc/config/CatalogBESpringConfig.java index 570cabee5f..0c78b1fdef 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/config/CatalogBESpringConfig.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/config/CatalogBESpringConfig.java @@ -65,7 +65,8 @@ import org.springframework.core.annotation.Order; "org.openecomp.sdc.be.servlets", "org.openecomp.sdc.be.filters", "org.openecomp.sdc.be.plugins", - "org.openecomp.sdc.be.togglz"}) + "org.openecomp.sdc.be.togglz", + "org.openecomp.sdc.be.ui.mapper"}) // @formatter:on public class CatalogBESpringConfig { diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogicTest.java index 07fff19f0b..5c8b6ce6d2 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogicTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ComponentInstanceBusinessLogicTest.java @@ -23,10 +23,12 @@ 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.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.DynamicTest.dynamicTest; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anySet; @@ -79,12 +81,12 @@ import org.openecomp.sdc.be.datatypes.elements.GetInputValueDataDefinition; import org.openecomp.sdc.be.datatypes.elements.GetPolicyValueDataDefinition; import org.openecomp.sdc.be.datatypes.elements.ListDataDefinition; import org.openecomp.sdc.be.datatypes.elements.RequirementDataDefinition; -import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition; import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum; import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields; import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum; import org.openecomp.sdc.be.datatypes.enums.OriginTypeEnum; import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum; +import org.openecomp.sdc.be.exception.BusinessException; import org.openecomp.sdc.be.impl.ComponentsUtils; import org.openecomp.sdc.be.model.ArtifactDefinition; import org.openecomp.sdc.be.model.CapabilityDefinition; @@ -111,6 +113,7 @@ import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache; import org.openecomp.sdc.be.model.jsonjanusgraph.config.ContainerInstanceTypesData; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ForwardingPathOperation; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade; +import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.be.model.operations.impl.GraphLockOperation; import org.openecomp.sdc.be.model.operations.impl.PropertyOperation; @@ -201,7 +204,7 @@ class ComponentInstanceBusinessLogicTest { @BeforeEach void init() { - MockitoAnnotations.initMocks(this); + MockitoAnnotations.openMocks(this); stubMethods(); createComponents(); } @@ -1950,6 +1953,216 @@ class ComponentInstanceBusinessLogicTest { } + @Test + void updateInstanceCapabilitySuccessTest() { + var containerComponentId = "containerComponentId"; + var componentInstanceUniqueId = "componentInstanceUniqueId"; + var capabilityDefinition = new CapabilityDefinition(); + capabilityDefinition.setUniqueId("uniqueId"); + + final Component component = new Service(); + component.setUniqueId(containerComponentId); + component.setLastUpdaterUserId(USER_ID); + component.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT); + + var componentInstance = new ComponentInstance(); + componentInstance.setUniqueId(componentInstanceUniqueId); + component.setComponentInstances(Collections.singletonList(componentInstance)); + + when(toscaOperationFacade.getToscaFullElement(containerComponentId)) + .thenReturn(Either.left(component)); + when(toscaOperationFacade.updateComponentInstanceCapability(containerComponentId, componentInstanceUniqueId, capabilityDefinition)) + .thenReturn(capabilityDefinition); + when(toscaOperationFacade.updateComponentInstanceMetadataOfTopologyTemplate(component)) + .thenReturn(Either.left(component)); + when(graphLockOperation.lockComponent(containerComponentId, NodeTypeEnum.Service)) + .thenReturn(StorageOperationStatus.OK); + + final Either resultEither = componentInstanceBusinessLogic + .updateInstanceCapability(ComponentTypeEnum.SERVICE, containerComponentId, componentInstanceUniqueId, capabilityDefinition, USER_ID); + assertTrue(resultEither.isLeft()); + final CapabilityDefinition actualCapabilityDefinition = resultEither.left().value(); + assertNotEquals(capabilityDefinition, actualCapabilityDefinition); + assertEquals(capabilityDefinition.getUniqueId(), actualCapabilityDefinition.getUniqueId()); + } + + @Test + void updateInstanceCapabilityNoContainerComponentTypeTest() { + var responseFormat = new ResponseFormat(); + when(componentsUtils.getResponseFormat(ActionStatus.NOT_ALLOWED)).thenReturn(responseFormat); + final Either resultEither = componentInstanceBusinessLogic + .updateInstanceCapability(null, "containerComponentId", "componentInstanceUniqueId", new CapabilityDefinition(), USER_ID); + assertTrue(resultEither.isRight(), "Either return should be right"); + final ResponseFormat actualResponseFormat = resultEither.right().value(); + assertEquals(responseFormat, actualResponseFormat); + } + + @Test + void updateInstanceCapabilityContainerComponentNotFoundTest() { + var containerComponentId = "containerComponentId"; + when(toscaOperationFacade.getToscaFullElement(containerComponentId)).thenReturn(Either.right(null)); + var responseFormat = new ResponseFormat(); + when(componentsUtils.getResponseFormat(ActionStatus.COMPONENT_NOT_FOUND, containerComponentId)).thenReturn(responseFormat); + final Either resultEither = componentInstanceBusinessLogic + .updateInstanceCapability(ComponentTypeEnum.SERVICE, "containerComponentId", "componentInstanceUniqueId", new CapabilityDefinition(), USER_ID); + assertTrue(resultEither.isRight(), "Either return should be right"); + final ResponseFormat actualResponseFormat = resultEither.right().value(); + assertEquals(responseFormat, actualResponseFormat); + } + + @Test + void updateInstanceCapabilityCannotWorkOnComponentTest() { + var containerComponentId = "containerComponentId"; + var componentInstanceUniqueId = "componentInstanceUniqueId"; + + final Component component = new Service(); + component.setUniqueId(containerComponentId); + component.setLastUpdaterUserId("anotherUse"); + component.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT); + + var expectedResponseFormat = new ResponseFormat(); + + when(toscaOperationFacade.getToscaFullElement(containerComponentId)) + .thenReturn(Either.left(component)); + when(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION)) + .thenReturn(expectedResponseFormat); + + final Either resultEither = componentInstanceBusinessLogic + .updateInstanceCapability(ComponentTypeEnum.SERVICE, containerComponentId, componentInstanceUniqueId, new CapabilityDefinition(), USER_ID); + assertTrue(resultEither.isRight(), "Either return should be right"); + final ResponseFormat actualResponseFormat = resultEither.right().value(); + assertEquals(expectedResponseFormat, actualResponseFormat); + } + + @Test + void updateInstanceCapabilityResourceInstanceNotFoundTest() { + var containerComponentId = "containerComponentId"; + var componentInstanceUniqueId = "componentInstanceUniqueId"; + + final Component component = new Service(); + component.setUniqueId(containerComponentId); + component.setLastUpdaterUserId(USER_ID); + component.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT); + + var expectedResponseFormat = new ResponseFormat(); + + when(toscaOperationFacade.getToscaFullElement(containerComponentId)) + .thenReturn(Either.left(component)); + when(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_INSTANCE_NOT_FOUND_ON_SERVICE, componentInstanceUniqueId, containerComponentId)) + .thenReturn(expectedResponseFormat); + + final Either resultEither = componentInstanceBusinessLogic + .updateInstanceCapability(ComponentTypeEnum.SERVICE, containerComponentId, componentInstanceUniqueId, new CapabilityDefinition(), USER_ID); + assertTrue(resultEither.isRight(), "Either return should be right"); + final ResponseFormat actualResponseFormat = resultEither.right().value(); + assertEquals(expectedResponseFormat, actualResponseFormat); + } + + @Test + void updateInstanceCapabilityUpdateMetadataFailTest() { + var containerComponentId = "containerComponentId"; + var componentInstanceUniqueId = "componentInstanceUniqueId"; + var capabilityDefinition = new CapabilityDefinition(); + capabilityDefinition.setUniqueId("uniqueId"); + + final Component component = new Service(); + component.setUniqueId(containerComponentId); + component.setLastUpdaterUserId(USER_ID); + component.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT); + + var componentInstance = new ComponentInstance(); + componentInstance.setUniqueId(componentInstanceUniqueId); + component.setComponentInstances(Collections.singletonList(componentInstance)); + + var expectedResponseFormat = new ResponseFormat(); + + when(toscaOperationFacade.getToscaFullElement(containerComponentId)) + .thenReturn(Either.left(component)); + when(graphLockOperation.lockComponent(containerComponentId, NodeTypeEnum.Service)) + .thenReturn(StorageOperationStatus.OK); + when(toscaOperationFacade.updateComponentInstanceCapability(containerComponentId, componentInstanceUniqueId, capabilityDefinition)) + .thenReturn(capabilityDefinition); + when(toscaOperationFacade.updateComponentInstanceMetadataOfTopologyTemplate(component)) + .thenReturn(Either.right(StorageOperationStatus.GENERAL_ERROR)); + when(componentsUtils.convertFromStorageResponse(StorageOperationStatus.GENERAL_ERROR, ComponentTypeEnum.SERVICE)) + .thenReturn(ActionStatus.GENERAL_ERROR); + when(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR)) + .thenReturn(expectedResponseFormat); + + final Either resultEither = componentInstanceBusinessLogic + .updateInstanceCapability(ComponentTypeEnum.SERVICE, containerComponentId, componentInstanceUniqueId, capabilityDefinition, USER_ID); + assertTrue(resultEither.isRight(), "Either return should be right"); + final ResponseFormat actualResponseFormat = resultEither.right().value(); + assertEquals(expectedResponseFormat, actualResponseFormat); + } + + @Test + void updateInstanceCapabilityBusinessExceptionHandlingTest() { + var containerComponentId = "containerComponentId"; + var componentInstanceUniqueId = "componentInstanceUniqueId"; + var capabilityDefinition = new CapabilityDefinition(); + capabilityDefinition.setUniqueId("uniqueId"); + + final Component component = new Service(); + component.setUniqueId(containerComponentId); + component.setLastUpdaterUserId(USER_ID); + component.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT); + + var componentInstance = new ComponentInstance(); + componentInstance.setUniqueId(componentInstanceUniqueId); + component.setComponentInstances(Collections.singletonList(componentInstance)); + + + when(toscaOperationFacade.getToscaFullElement(containerComponentId)) + .thenReturn(Either.left(component)); + when(graphLockOperation.lockComponent(containerComponentId, NodeTypeEnum.Service)) + .thenReturn(StorageOperationStatus.OK); + when(toscaOperationFacade.updateComponentInstanceCapability(containerComponentId, componentInstanceUniqueId, capabilityDefinition)) + .thenThrow(new OperationException(ActionStatus.GENERAL_ERROR)); + + final BusinessException businessException = assertThrows(BusinessException.class, () -> { + componentInstanceBusinessLogic + .updateInstanceCapability(ComponentTypeEnum.SERVICE, containerComponentId, componentInstanceUniqueId, capabilityDefinition, USER_ID); + }); + assertTrue(businessException instanceof OperationException); + assertEquals(ActionStatus.GENERAL_ERROR, ((OperationException) businessException).getActionStatus()); + } + + @Test + void updateInstanceCapabilityUnknownExceptionHandlingTest() { + var containerComponentId = "containerComponentId"; + var componentInstanceUniqueId = "componentInstanceUniqueId"; + var capabilityDefinition = new CapabilityDefinition(); + capabilityDefinition.setUniqueId("uniqueId"); + + final Component component = new Service(); + component.setUniqueId(containerComponentId); + component.setLastUpdaterUserId(USER_ID); + component.setLifecycleState(LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT); + + var componentInstance = new ComponentInstance(); + componentInstance.setUniqueId(componentInstanceUniqueId); + component.setComponentInstances(Collections.singletonList(componentInstance)); + + var expectedResponseFormat = new ResponseFormat(); + + when(toscaOperationFacade.getToscaFullElement(containerComponentId)) + .thenReturn(Either.left(component)); + when(graphLockOperation.lockComponent(containerComponentId, NodeTypeEnum.Service)) + .thenReturn(StorageOperationStatus.OK); + when(toscaOperationFacade.updateComponentInstanceCapability(containerComponentId, componentInstanceUniqueId, capabilityDefinition)) + .thenThrow(new RuntimeException()); + when(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR)) + .thenReturn(expectedResponseFormat); + + final Exception exception = assertThrows(BusinessException.class, () -> + componentInstanceBusinessLogic + .updateInstanceCapability(ComponentTypeEnum.SERVICE, containerComponentId, componentInstanceUniqueId, capabilityDefinition, USER_ID)); + assertTrue(exception instanceof ByResponseFormatComponentException); + final ByResponseFormatComponentException actualException = (ByResponseFormatComponentException) exception; + assertEquals(expectedResponseFormat, actualException.getResponseFormat()); + } + private ComponentInstance createServiceSubstitutionComponentInstance() { final ComponentInstance instanceToBeCreated = new ComponentInstance(); instanceToBeCreated.setName(COMPONENT_INSTANCE_NAME); diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ComponentInstanceCapabilityServletTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ComponentInstanceCapabilityServletTest.java new file mode 100644 index 0000000000..8155f22d65 --- /dev/null +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ComponentInstanceCapabilityServletTest.java @@ -0,0 +1,240 @@ +/* + * ============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.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import fj.data.Either; +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.glassfish.jersey.server.ResourceConfig; +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.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic; +import org.openecomp.sdc.be.config.ConfigurationManager; +import org.openecomp.sdc.be.config.ErrorInfo; +import org.openecomp.sdc.be.dao.api.ActionStatus; +import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum; +import org.openecomp.sdc.be.model.CapabilityDefinition; +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.mapper.CapabilityMapper; +import org.openecomp.sdc.be.ui.model.ComponentInstanceCapabilityUpdateModel; +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.openecomp.sdc.exception.ServiceException; + +class ComponentInstanceCapabilityServletTest extends JerseySpringBaseTest { + + private static final String UPDATE_INSTANCE_REQUIREMENT_PATH_FORMAT = "/v1/catalog/%s/%s/componentInstances/%s/capability"; + private static ConfigurationManager configurationManager; + + private final String componentId = "componentId"; + private final String componentInstanceId = "componentInstanceId"; + private final String userId = "userId"; + + private CapabilityMapper capabilityMapper; + private ComponentInstanceBusinessLogic componentInstanceBusinessLogicMock; + + @Override + protected ResourceConfig configure() { + componentInstanceBusinessLogicMock = mock(ComponentInstanceBusinessLogic.class); + capabilityMapper = new CapabilityMapper(); + var servletResponseBuilder = new ServletResponseBuilder(); + var componentInstanceCapabilityServlet = + new ComponentInstanceCapabilityServlet(componentInstanceBusinessLogicMock, capabilityMapper, servletResponseBuilder); + return super.configure().register(componentInstanceCapabilityServlet) + .register(new OperationExceptionMapper(servletResponseBuilder)); + } + + @BeforeAll + static void beforeAll() { + setupConfiguration(); + } + + private static void setupConfiguration() { + final ConfigurationSource configurationSource = + new FSConfigurationSource(ExternalConfiguration.getChangeListener(), "src/test/resources/config/catalog-be"); + configurationManager = new ConfigurationManager(configurationSource); + } + + //workaround for JerseyTest + Junit5 + @BeforeEach + void beforeEach() throws Exception { + super.setUp(); + } + + //workaround for JerseyTest + Junit5 + @AfterEach + void afterEach() throws Exception { + super.tearDown(); + } + + @Test + void updateInstanceRequirementSuccessTest() throws JsonProcessingException { + final var updateModel = createDefaultUpdateMode(); + var expectedCapabilityDefinition = capabilityMapper.mapToCapabilityDefinition(updateModel); + + when(componentInstanceBusinessLogicMock + .updateInstanceCapability(eq(ComponentTypeEnum.SERVICE), eq(componentId), eq(componentInstanceId), any(CapabilityDefinition.class), eq(userId))) + .thenReturn(Either.left(expectedCapabilityDefinition)); + final var url = + String.format(UPDATE_INSTANCE_REQUIREMENT_PATH_FORMAT, ComponentTypeEnum.SERVICE_PARAM_NAME, componentId, componentInstanceId); + + final Response response = target() + .path(url) + .request(MediaType.APPLICATION_JSON) + .header(Constants.USER_ID_HEADER, userId) + .put(Entity.entity(parseToJson(updateModel), MediaType.APPLICATION_JSON)); + var actualCapabilityDefinition = response.readEntity(CapabilityDefinition.class); + assertEquals(200, response.getStatus(), "The update status should be as expected"); + assertEquals(MediaType.valueOf(MediaType.APPLICATION_JSON), response.getMediaType(), "The content type should be application/json"); + assertCapabilityDefinition(actualCapabilityDefinition, expectedCapabilityDefinition); + } + + @Test + void updateInstanceRequirementFailTest() throws JsonProcessingException { + final var expectedResponseFormat = new ResponseFormat(404); + final var requestErrorWrapper = expectedResponseFormat.new RequestErrorWrapper(); + final var serviceException = new ServiceException("anErrorCode", "anErrorText", new String[2]); + requestErrorWrapper.setServiceException(serviceException); + expectedResponseFormat.setRequestError(requestErrorWrapper); + + when(componentInstanceBusinessLogicMock + .updateInstanceCapability(eq(ComponentTypeEnum.SERVICE), eq(componentId), eq(componentInstanceId), any(CapabilityDefinition.class), eq(userId))) + .thenReturn(Either.right(expectedResponseFormat)); + + final var url = + String.format(UPDATE_INSTANCE_REQUIREMENT_PATH_FORMAT, ComponentTypeEnum.SERVICE_PARAM_NAME, componentId, componentInstanceId); + final Response response = target() + .path(url) + .request(MediaType.APPLICATION_JSON) + .header(Constants.USER_ID_HEADER, userId) + .put(Entity.entity(parseToJson(createDefaultUpdateMode()), MediaType.APPLICATION_JSON)); + var actualResponseFormat = response.readEntity(ResponseFormat.class); + + assertEquals(expectedResponseFormat.getStatus(), response.getStatus(), "The update status should be as expected"); + assertNotNull(actualResponseFormat.getRequestError()); + assertNotNull(actualResponseFormat.getRequestError().getRequestError()); + assertEquals(expectedResponseFormat.getMessageId(), actualResponseFormat.getMessageId()); + assertEquals(expectedResponseFormat.getVariables().length, actualResponseFormat.getVariables().length); + } + + @Test + void updateInstanceRequirementKnownErrorTest() throws JsonProcessingException { + when(componentInstanceBusinessLogicMock + .updateInstanceCapability(eq(ComponentTypeEnum.SERVICE), eq(componentId), eq(componentInstanceId), any(CapabilityDefinition.class), eq(userId))) + .thenThrow(new OperationException(ActionStatus.COMPONENT_NOT_FOUND, componentId)); + final var url = + String.format(UPDATE_INSTANCE_REQUIREMENT_PATH_FORMAT, ComponentTypeEnum.SERVICE_PARAM_NAME, componentId, componentInstanceId); + final Response response = target() + .path(url) + .request(MediaType.APPLICATION_JSON) + .header(Constants.USER_ID_HEADER, userId) + .put(Entity.entity(parseToJson(createDefaultUpdateMode()), MediaType.APPLICATION_JSON)); + var responseFormat = response.readEntity(ResponseFormat.class); + + final ErrorInfo errorInfo = configurationManager.getErrorConfiguration().getErrorInfo(ActionStatus.COMPONENT_NOT_FOUND.name()); + assertNotNull(errorInfo); + assertEquals(errorInfo.getCode(), response.getStatus(), "The update status should be as expected"); + assertEquals(errorInfo.getMessageId(), responseFormat.getMessageId()); + assertEquals(errorInfo.getMessage(), responseFormat.getText()); + } + + @Test + void updateInstanceRequirementUnknownErrorTest() throws JsonProcessingException { + when(componentInstanceBusinessLogicMock + .updateInstanceCapability(eq(ComponentTypeEnum.SERVICE), eq(componentId), eq(componentInstanceId), any(CapabilityDefinition.class), eq(userId))) + .thenThrow(new RuntimeException()); + final var url = + String.format(UPDATE_INSTANCE_REQUIREMENT_PATH_FORMAT, ComponentTypeEnum.SERVICE_PARAM_NAME, componentId, componentInstanceId); + final Response response = target() + .path(url) + .request(MediaType.APPLICATION_JSON) + .header(Constants.USER_ID_HEADER, userId) + .put(Entity.entity(parseToJson(createDefaultUpdateMode()), MediaType.APPLICATION_JSON)); + var responseFormat = response.readEntity(ResponseFormat.class); + + final ErrorInfo errorInfo = configurationManager.getErrorConfiguration().getErrorInfo(ActionStatus.GENERAL_ERROR.name()); + assertNotNull(errorInfo); + assertEquals(errorInfo.getCode(), response.getStatus(), "The update status should be as expected"); + assertEquals(errorInfo.getMessageId(), responseFormat.getMessageId()); + assertEquals(errorInfo.getMessage(), responseFormat.getText()); + } + + @Test + void updateInstanceRequirementIncorrectComponentTypeTest() throws JsonProcessingException { + final var url = + String.format(UPDATE_INSTANCE_REQUIREMENT_PATH_FORMAT, "wrongType", componentId, componentInstanceId); + final Response response = target() + .path(url) + .request(MediaType.APPLICATION_JSON) + .header(Constants.USER_ID_HEADER, userId) + .put(Entity.entity(parseToJson(createDefaultUpdateMode()), MediaType.APPLICATION_JSON)); + var responseFormat = response.readEntity(ResponseFormat.class); + + final ErrorInfo errorInfo = configurationManager.getErrorConfiguration().getErrorInfo(ActionStatus.UNSUPPORTED_ERROR.name()); + assertNotNull(errorInfo); + assertEquals(errorInfo.getCode(), response.getStatus(), "The update status should be as expected"); + assertEquals(errorInfo.getMessageId(), responseFormat.getMessageId()); + assertEquals(errorInfo.getMessage(), responseFormat.getText()); + } + + private ComponentInstanceCapabilityUpdateModel createDefaultUpdateMode() { + var capabilityUpdateModel = new ComponentInstanceCapabilityUpdateModel(); + capabilityUpdateModel.setName("name"); + capabilityUpdateModel.setExternal(true); + capabilityUpdateModel.setOwnerId("ownerId"); + capabilityUpdateModel.setType("type"); + capabilityUpdateModel.setOwnerName("ownerName"); + capabilityUpdateModel.setUniqueId("uniqueId"); + return capabilityUpdateModel; + } + + private void assertCapabilityDefinition(final CapabilityDefinition actualCapabilityDefinition, + final CapabilityDefinition expectedCapabilityDefinition) { + assertEquals(expectedCapabilityDefinition.getUniqueId(), actualCapabilityDefinition.getUniqueId()); + assertEquals(expectedCapabilityDefinition.getName(), actualCapabilityDefinition.getName()); + assertEquals(expectedCapabilityDefinition.getOwnerId(), actualCapabilityDefinition.getOwnerId()); + assertEquals(expectedCapabilityDefinition.getOwnerName(), actualCapabilityDefinition.getOwnerName()); + assertEquals(expectedCapabilityDefinition.getType(), actualCapabilityDefinition.getType()); + assertEquals(expectedCapabilityDefinition.isExternal(), actualCapabilityDefinition.isExternal()); + } + + private String parseToJson(final T object) throws JsonProcessingException { + return new ObjectMapper().writeValueAsString(object); + } + +} + diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/JerseySpringBaseTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/JerseySpringBaseTest.java index e755de12f0..7fda4edd4f 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/JerseySpringBaseTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/JerseySpringBaseTest.java @@ -77,6 +77,7 @@ public abstract class JerseySpringBaseTest extends JerseyTest { .register(jacksonJsonProvider); } + @Override protected ResourceConfig configure() { forceSet(TestProperties.CONTAINER_PORT, "0"); return configure(BaseTestConfig.class); diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/CapabilityRequirementConverterTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/CapabilityRequirementConverterTest.java index 49920c3c45..5f1b62d1d4 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/CapabilityRequirementConverterTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/CapabilityRequirementConverterTest.java @@ -307,6 +307,7 @@ public class CapabilityRequirementConverterTest { CapabilityDefinition capabilityDefinition = new CapabilityDefinition(); capabilityDefinition.setName(capabilityName); capabilityDefinition.setType("att.Node"); + capabilityDefinition.setExternal(true); List properties = new ArrayList<>(); ComponentInstanceProperty prop = new ComponentInstanceProperty(); prop.setName(PROPERTY_NAME); diff --git a/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml b/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml index 50906d2624..b363bacd73 100644 --- a/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml +++ b/catalog-be/src/test/resources/config/catalog-be/error-configuration.yaml @@ -2266,3 +2266,31 @@ errors: message: "Error: Invalid content. Type name is not defined for policy %1", messageId: "SVC4802" } + #---------SVC4140------------------------------ + # %1 - Component uid + COMPONENT_FIND_ERROR: { + code: 500, + message: "An unexpected error occurred while retrieving the component '%1'.", + messageId: "SVC4140" + } + #---------SVC4141------------------------------ + # %1 - Component uid + COMPONENT_CAPABILITIES_FIND_ERROR: { + code: 500, + message: "An unexpected error occurred while retrieving the component '%1' capabilities.", + messageId: "SVC4141" + } + #---------SVC4142------------------------------ + # %1 - Component uid or name + COMPONENT_NOT_FOUND: { + code: 404, + message: "Component '%1' was not found.", + messageId: "SVC4142" + } + #---------SVC4143------------------------------ + # %1 - Capability name + COMPONENT_INSTANCE_CAPABILITY_UPDATE_ERROR: { + code: 500, + message: "An unexpected error occurred while updating the capability '%1'.", + messageId: "SVC4143" + } \ No newline at end of file -- cgit 1.2.3-korg