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 +++ .../org/openecomp/sdc/be/dao/api/ActionStatus.java | 5 + .../operations/NodeTemplateOperation.java | 124 ++++++++++- .../operations/ToscaOperationFacade.java | 7 + .../operations/exception/OperationException.java | 42 ++++ .../sdc/be/ui/mapper/CapabilityMapper.java | 40 ++++ .../ComponentInstanceCapabilityUpdateModel.java | 50 +++++ .../operations/NodeTemplateOperationTest.java | 197 +++++++++++++---- .../sdc/be/ui/mapper/CapabilityMapperTest.java | 60 ++++++ catalog-ui/src/app/models/capability.ts | 2 + .../graph/composition-graph.component.ts | 11 +- .../req-capabilities-tab.component.html | 28 ++- .../req-capabilities-tab.component.ts | 42 +++- .../requirement-list.component.html | 2 +- .../component-instance.service.spec.ts | 80 +++++++ .../component-instance.service.ts | 36 +++- catalog-ui/src/app/utils/constants.ts | 1 + .../openecomp/sdc/exception/ResponseFormat.java | 10 +- .../log/enums/LoggerSupportabilityActions.java | 1 + .../elements/CapabilityDataDefinition.java | 21 +- .../sdc/be/exception/BusinessException.java | 34 +++ .../CompositionRequirementsCapabilitiesTab.java | 7 +- 35 files changed, 1573 insertions(+), 93 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 create mode 100644 catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/exception/OperationException.java create mode 100644 catalog-model/src/main/java/org/openecomp/sdc/be/ui/mapper/CapabilityMapper.java create mode 100644 catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/ComponentInstanceCapabilityUpdateModel.java create mode 100644 catalog-model/src/test/java/org/openecomp/sdc/be/ui/mapper/CapabilityMapperTest.java create mode 100644 catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.spec.ts create mode 100644 common-be/src/main/java/org/openecomp/sdc/be/exception/BusinessException.java 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 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 a8f94e8eb5..619f88263c 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 @@ -31,6 +31,10 @@ public enum ActionStatus { RESOURCE_NOT_FOUND, MISSING_DERIVED_FROM_TEMPLATE, PARENT_RESOURCE_NOT_FOUND, PARENT_RESOURCE_DOES_NOT_EXTEND, INVALID_DEFAULT_VALUE, INVALID_COMPLEX_DEFAULT_VALUE, MULTIPLE_PARENT_RESOURCE_FOUND, INVALID_RESOURCE_PAYLOAD, INVALID_TOSCA_FILE_EXTENSION, INVALID_YAML_FILE, INVALID_TOSCA_TEMPLATE, NOT_RESOURCE_TOSCA_TEMPLATE, NOT_SINGLE_RESOURCE, INVALID_RESOURCE_NAMESPACE, RESOURCE_ALREADY_EXISTS, INVALID_RESOURCE_CHECKSUM, RESOURCE_CANNOT_CONTAIN_RESOURCE_INSTANCES, NO_ASSETS_FOUND, GENERIC_TYPE_NOT_FOUND, INVALID_RESOURCE_TYPE, TOSCA_PARSE_ERROR, INVALID_PM_DICTIONARY_FILE, // Service related SERVICE_TYPE_EXCEEDS_LIMIT, INVALID_SERVICE_TYPE, SERVICE_ROLE_EXCEEDS_LIMIT, INVALID_SERVICE_ROLE, INVALID_INSTANTIATION_TYPE, NOT_SERVICE_TOSCA_TEMPLATE, NOT_SINGLE_SERVICE, INVALID_SERVICE_CHECKSUM, INVALID_SERVICE_PAYLOAD, INVALID_SERVICE_NAMESPACE, SERVICE_ALREADY_EXISTS, UNSUPPORTED_DISTRIBUTION_STATUS, INVALID_NAMING_POLICY, INVALID_ENVIRONMENT_CONTEXT, NAMING_POLICY_EXCEEDS_LIMIT, MISSING_ECOMP_GENERATED_NAMING, PROPERTY_EXCEEDS_LIMIT, INVALID_PROPERY, + // Component related + COMPONENT_FIND_ERROR, + COMPONENT_CAPABILITIES_FIND_ERROR, + COMPONENT_INSTANCE_CAPABILITY_UPDATE_ERROR, // Component name related COMPONENT_NAME_ALREADY_EXIST, COMPONENT_NAME_EXCEEDS_LIMIT, MISSING_COMPONENT_NAME, INVALID_COMPONENT_NAME, // Component description related @@ -109,6 +113,7 @@ public enum ActionStatus { CONTAINER_CANNOT_CONTAIN_COMPONENT_IN_STATE, CONTAINER_CANNOT_CONTAIN_INSTANCE, MISSING_MANDATORY_PROPERTY, MANDATORY_PROPERTY_MISSING_VALUE, //Capability related CAPABILITY_NOT_FOUND, CAPABILITY_NAME_MANDATORY, CAPABILITY_TYPE_MANDATORY, CAPABILITY_NAME_ALREADY_IN_USE, MAX_OCCURRENCES_SHOULD_BE_GREATER_THAN_MIN_OCCURRENCES, CAPABILITY_DELETION_NOT_ALLOWED_USED_IN_COMPOSITION, CAPABILITY_UPDATE_NOT_ALLOWED_USED_IN_COMPOSITION, INVALID_CAPABILITY_NAME, FAILED_TO_CREATE_OR_UPDATE_CAPABILITY_PROPERTIES, CAPABILITY_PROPERTIES_NOT_FOUND, RELATIONSHIP_TYPE_ALREADY_EXIST, MISSING_RELATIONSHIP_TYPE, CAPABILITY_TYPE_CANNOT_BE_EMPTY, + COMPONENT_CAPABILITY_UPDATE_NOT_FOUND_ERROR, //Requirement related REQUIREMENT_NOT_FOUND, REQUIREMENT_NAME_MANDATORY, REQUIREMENT_CAPABILITY_MANDATORY, REQUIREMENT_NAME_ALREADY_IN_USE, REQUIREMENT_DELETION_NOT_ALLOWED_USED_IN_COMPOSITION, REQUIREMENT_UPDATE_NOT_ALLOWED_USED_IN_COMPOSITION, INVALID_REQUIREMENT_NAME, //Abstract template related diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java index f9e9a46698..416c701582 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperation.java @@ -45,6 +45,7 @@ import org.apache.tinkerpop.gremlin.structure.Edge; import org.janusgraph.core.JanusGraphVertex; import org.openecomp.sdc.be.config.BeEcompErrorManager; import org.openecomp.sdc.be.config.ConfigurationManager; +import org.openecomp.sdc.be.dao.api.ActionStatus; import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus; import org.openecomp.sdc.be.dao.jsongraph.GraphVertex; import org.openecomp.sdc.be.dao.jsongraph.types.EdgeLabelEnum; @@ -105,6 +106,7 @@ import org.openecomp.sdc.be.model.jsonjanusgraph.datamodel.TopologyTemplate; import org.openecomp.sdc.be.model.jsonjanusgraph.datamodel.ToscaElement; import org.openecomp.sdc.be.model.jsonjanusgraph.datamodel.ToscaElementTypeEnum; import org.openecomp.sdc.be.model.jsonjanusgraph.enums.JsonConstantKeysEnum; +import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; import org.openecomp.sdc.be.model.jsonjanusgraph.utils.ModelConverter; import org.openecomp.sdc.be.model.operations.StorageException; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; @@ -115,7 +117,9 @@ import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum; import org.openecomp.sdc.common.api.ArtifactTypeEnum; 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.elements.LoggerSupportability; +import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode; import org.openecomp.sdc.common.log.enums.LogLevel; import org.openecomp.sdc.common.log.enums.LoggerSupportabilityActions; import org.openecomp.sdc.common.log.enums.StatusCode; @@ -957,11 +961,13 @@ public class NodeTemplateOperation extends BaseOperation { MapListCapabilityDataDefinition allCalculatedCap = new MapListCapabilityDataDefinition(); if (calculatedCapabilities != null) { calculatedCapabilities.forEach((key1, value1) -> { - Map mapByType = value1.getMapToscaDataDefinition(); - mapByType.forEach((key, value) -> value.getListToscaDataDefinition().forEach(cap -> { - cap.addToPath(componentInstance.getUniqueId()); - allCalculatedCap.add(key, cap); - })); + final Map mapByType = value1.getMapToscaDataDefinition(); + mapByType.forEach((key, value) -> value.getListToscaDataDefinition().stream() + .filter(CapabilityDataDefinition::isExternal) + .forEach(cap -> { + cap.addToPath(componentInstance.getUniqueId()); + allCalculatedCap.add(key, cap); + })); }); } MapListCapabilityDataDefinition allCaps; @@ -1068,6 +1074,93 @@ public class NodeTemplateOperation extends BaseOperation { return StorageOperationStatus.OK; } + public CapabilityDataDefinition updateComponentInstanceCapabilities(final String componentId, final String componentInstanceUniqueId, + final CapabilityDataDefinition capabilityDataDefinition) { + + final GraphVertex containerVertex = findComponentVertex(componentId).orElse(null); + if (containerVertex == null) { + throw new OperationException(ActionStatus.COMPONENT_NOT_FOUND, componentId); + } + + final Pair> capabilityVertexAndDataPair = + findCapabilityVertex(containerVertex).orElse(null); + if (capabilityVertexAndDataPair == null) { + throw new OperationException(ActionStatus.CAPABILITY_OF_INSTANCE_NOT_FOUND_ON_CONTAINER, capabilityDataDefinition.getName(), + capabilityDataDefinition.getOwnerName(), componentId); + } + final GraphVertex capabilitiesVertex = capabilityVertexAndDataPair.getLeft(); + final Map capabilityDataMap = capabilityVertexAndDataPair.getRight(); + + final CapabilityDataDefinition actualCapabilityDataDefinition = + findCapability(capabilityDataMap, componentInstanceUniqueId, capabilityDataDefinition.getType(), capabilityDataDefinition.getUniqueId()) + .orElse(null); + if (actualCapabilityDataDefinition == null) { + throw new OperationException(ActionStatus.CAPABILITY_OF_INSTANCE_NOT_FOUND_ON_CONTAINER, capabilityDataDefinition.getName(), + capabilityDataDefinition.getOwnerName(), componentId); + } + bindCapabilityDefinition(capabilityDataDefinition, actualCapabilityDataDefinition); + + var storageOperationStatus = updateCapabilityVertex(containerVertex, capabilitiesVertex); + if (storageOperationStatus != StorageOperationStatus.OK) { + throw new OperationException(ActionStatus.COMPONENT_INSTANCE_CAPABILITY_UPDATE_ERROR, capabilityDataDefinition.getName()); + } + return new CapabilityDataDefinition(actualCapabilityDataDefinition); + } + + private void bindCapabilityDefinition(final CapabilityDataDefinition fromCapabilityDefinition, + final CapabilityDataDefinition capabilityDefinitionToUpdate) { + capabilityDefinitionToUpdate.setExternal(fromCapabilityDefinition.isExternal()); + } + + private Optional findComponentVertex(final String componentId) { + final Either componentEither = janusGraphDao.getVertexById(componentId, JsonParseFlagEnum.ParseAll); + if (componentEither.isRight()) { + final JanusGraphOperationStatus error = componentEither.right().value(); + CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, FAILED_TO_FETCH_CONTAINER_VERTEX_ERROR, componentId, error); + if (error == JanusGraphOperationStatus.NOT_FOUND) { + return Optional.empty(); + } + log.error(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR, NodeTemplateOperation.class.getName(), (ErrorLogOptionalData) null, + FAILED_TO_FETCH_CONTAINER_VERTEX_ERROR, componentId, error); + throw new OperationException(ActionStatus.COMPONENT_FIND_ERROR, componentId); + } + return Optional.ofNullable(componentEither.left().value()); + } + + private Optional>> findCapabilityVertex(final GraphVertex containerVertex) { + + final Either>, StorageOperationStatus> capabilitiesEither = + fetchContainerCalculatedCapability(containerVertex, EdgeLabelEnum.CALCULATED_CAPABILITIES); + if (capabilitiesEither.isRight()) { + final StorageOperationStatus error = capabilitiesEither.right().value(); + if (error == StorageOperationStatus.NOT_FOUND) { + return Optional.empty(); + } + log.error(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR, NodeTemplateOperation.class.getName(), (ErrorLogOptionalData) null, + FAILED_TO_FETCH_CONTAINER_VERTEX_ERROR, containerVertex.getUniqueId(), error); + throw new OperationException(ActionStatus.COMPONENT_CAPABILITIES_FIND_ERROR, containerVertex.getUniqueId()); + } + return Optional.ofNullable(capabilitiesEither.left().value()); + } + + private Optional findCapability(final Map capabilityMap, + final String componentInstanceUniqueId, + final String capabilityType, + final String capabilityUniqueId) { + var componentInstanceCapabilitiesMap = capabilityMap.get(componentInstanceUniqueId); + if (componentInstanceCapabilitiesMap == null || componentInstanceCapabilitiesMap.isEmpty()) { + return Optional.empty(); + } + var listCapabilityDataDefinition = componentInstanceCapabilitiesMap.getMapToscaDataDefinition().get(capabilityType); + if (listCapabilityDataDefinition == null || listCapabilityDataDefinition.isEmpty()) { + return Optional.empty(); + } + + return listCapabilityDataDefinition.getListToscaDataDefinition().stream() + .filter(e -> e.getUniqueId().equals(capabilityUniqueId)) + .findFirst(); + } + public StorageOperationStatus updateComponentInstanceRequirement(String componentId, String componentInstanceUniqueId, RequirementDataDefinition requirementDataDefinition) { Either containerVEither = janusGraphDao @@ -1143,6 +1236,27 @@ public class NodeTemplateOperation extends BaseOperation { return StorageOperationStatus.OK; } + private StorageOperationStatus updateCapabilityVertex(final GraphVertex containerVertex, final GraphVertex capabilitiesVertex) { + containerVertex.setJsonMetadataField(JsonPresentationFields.LAST_UPDATE_DATE, System.currentTimeMillis()); + final Either updateElement = janusGraphDao.updateVertex(containerVertex); + if (updateElement.isRight()) { + CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Failed to update topology template '{}' with new capabilities. Error '{}'.", + containerVertex.getUniqueId(), updateElement.right().value()); + return DaoStatusConverter.convertJanusGraphStatusToStorageStatus(updateElement.right().value()); + } + + final Either statusEither = + updateOrCopyOnUpdate(capabilitiesVertex, containerVertex, EdgeLabelEnum.CALCULATED_CAPABILITIES); + if (statusEither.isRight()) { + JanusGraphOperationStatus error = statusEither.right().value(); + CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, + "Failed to update calculated capability for container {} error {}", containerVertex.getUniqueId(), error); + return DaoStatusConverter.convertJanusGraphStatusToStorageStatus(error); + } + CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Updated calculated capability for container '{}'", containerVertex.getUniqueId()); + return StorageOperationStatus.OK; + } + private StorageOperationStatus addComponentInstanceToscaDataToNodeTypeContainer(NodeType originNodeType, ComponentInstanceDataDefinition componentInstance, GraphVertex updatedContainerVertex) { diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/ToscaOperationFacade.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/ToscaOperationFacade.java index 06e23f04ea..205a48e9b2 100644 --- a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/ToscaOperationFacade.java +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/ToscaOperationFacade.java @@ -112,6 +112,7 @@ import org.openecomp.sdc.be.model.catalog.CatalogComponent; import org.openecomp.sdc.be.model.jsonjanusgraph.config.ContainerInstanceTypesData; import org.openecomp.sdc.be.model.jsonjanusgraph.datamodel.TopologyTemplate; import org.openecomp.sdc.be.model.jsonjanusgraph.datamodel.ToscaElement; +import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; import org.openecomp.sdc.be.model.jsonjanusgraph.utils.ModelConverter; import org.openecomp.sdc.be.model.operations.StorageException; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; @@ -2963,6 +2964,12 @@ public class ToscaOperationFacade { return nodeTemplateOperation.updateComponentInstanceRequirement(containerComponentId, componentInstanceUniqueId, requirementDataDefinition); } + public CapabilityDataDefinition updateComponentInstanceCapability(final String containerComponentId, final String componentInstanceUniqueId, + final CapabilityDataDefinition capabilityDataDefinition) { + + return nodeTemplateOperation.updateComponentInstanceCapabilities(containerComponentId, componentInstanceUniqueId, capabilityDataDefinition); + } + public StorageOperationStatus updateComponentInstanceInterfaces(Component containerComponent, String componentInstanceUniqueId) { MapInterfaceDataDefinition mapInterfaceDataDefinition = convertComponentInstanceInterfaces(containerComponent, componentInstanceUniqueId); return topologyTemplateOperation.updateComponentInstanceInterfaces(containerComponent, componentInstanceUniqueId, mapInterfaceDataDefinition); diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/exception/OperationException.java b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/exception/OperationException.java new file mode 100644 index 0000000000..30323af521 --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/exception/OperationException.java @@ -0,0 +1,42 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception; + +import lombok.Getter; +import org.openecomp.sdc.be.dao.api.ActionStatus; +import org.openecomp.sdc.be.exception.BusinessException; + +@Getter +public class OperationException extends BusinessException { + + private final ActionStatus actionStatus; + private final String[] params; + + public OperationException(final String message) { + super(message); + actionStatus = ActionStatus.GENERAL_ERROR; + params = new String[0]; + } + + public OperationException(final ActionStatus actionStatus, String... params) { + this.actionStatus = actionStatus; + this.params = params; + } +} diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/ui/mapper/CapabilityMapper.java b/catalog-model/src/main/java/org/openecomp/sdc/be/ui/mapper/CapabilityMapper.java new file mode 100644 index 0000000000..4df93829fa --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/ui/mapper/CapabilityMapper.java @@ -0,0 +1,40 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.ui.mapper; + +import org.openecomp.sdc.be.model.CapabilityDefinition; +import org.openecomp.sdc.be.ui.model.ComponentInstanceCapabilityUpdateModel; +import org.springframework.stereotype.Service; + +@Service +public class CapabilityMapper { + + public CapabilityDefinition mapToCapabilityDefinition(final ComponentInstanceCapabilityUpdateModel capabilityUpdateModel) { + var capabilityDefinition = new CapabilityDefinition(); + capabilityDefinition.setUniqueId(capabilityUpdateModel.getUniqueId()); + capabilityDefinition.setType(capabilityUpdateModel.getType()); + capabilityDefinition.setOwnerId(capabilityUpdateModel.getOwnerId()); + capabilityDefinition.setOwnerName(capabilityUpdateModel.getOwnerName()); + capabilityDefinition.setName(capabilityUpdateModel.getName()); + capabilityDefinition.setExternal(capabilityUpdateModel.getExternal()); + return capabilityDefinition; + } + +} diff --git a/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/ComponentInstanceCapabilityUpdateModel.java b/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/ComponentInstanceCapabilityUpdateModel.java new file mode 100644 index 0000000000..3fd74cd9e2 --- /dev/null +++ b/catalog-model/src/main/java/org/openecomp/sdc/be/ui/model/ComponentInstanceCapabilityUpdateModel.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.ui.model; + +import javax.validation.constraints.NotNull; +import javax.validation.constraints.Size; +import lombok.Data; + +/** + * Model for the component instance capability update request + */ +@Data +public class ComponentInstanceCapabilityUpdateModel { + + @NotNull + @Size(min=1) + private String type; + @NotNull + @Size(min=1) + private String name; + @NotNull + @Size(min=1) + private String ownerId; + @NotNull + @Size(min=1) + private String ownerName; + @NotNull + @Size(min=1) + private String uniqueId; + @NotNull + private Boolean external; + +} diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperationTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperationTest.java index cf4d6b22bf..5a02c5752f 100644 --- a/catalog-model/src/test/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperationTest.java +++ b/catalog-model/src/test/java/org/openecomp/sdc/be/model/jsonjanusgraph/operations/NodeTemplateOperationTest.java @@ -29,12 +29,29 @@ */ package org.openecomp.sdc.be.model.jsonjanusgraph.operations; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +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.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import com.google.common.collect.Lists; import fj.data.Either; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.tinkerpop.gremlin.structure.Direction; import org.apache.tinkerpop.gremlin.structure.Edge; -import org.apache.tinkerpop.gremlin.structure.Vertex; import org.janusgraph.core.JanusGraphVertex; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; @@ -43,46 +60,48 @@ import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentMatchers; import org.mockito.InjectMocks; -import org.mockito.Mock; import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; import org.mockito.junit.jupiter.MockitoExtension; +import org.openecomp.sdc.be.config.ConfigurationManager; +import org.openecomp.sdc.be.dao.api.ActionStatus; import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus; import org.openecomp.sdc.be.dao.jsongraph.GraphVertex; import org.openecomp.sdc.be.dao.jsongraph.JanusGraphDao; import org.openecomp.sdc.be.dao.jsongraph.types.EdgeLabelEnum; import org.openecomp.sdc.be.dao.jsongraph.types.JsonParseFlagEnum; import org.openecomp.sdc.be.dao.jsongraph.types.VertexTypeEnum; -import org.openecomp.sdc.be.datatypes.elements.*; +import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition; +import org.openecomp.sdc.be.datatypes.elements.CapabilityDataDefinition; +import org.openecomp.sdc.be.datatypes.elements.ComponentInstanceDataDefinition; +import org.openecomp.sdc.be.datatypes.elements.ListCapabilityDataDefinition; +import org.openecomp.sdc.be.datatypes.elements.ListRequirementDataDefinition; +import org.openecomp.sdc.be.datatypes.elements.MapArtifactDataDefinition; +import org.openecomp.sdc.be.datatypes.elements.MapListCapabilityDataDefinition; +import org.openecomp.sdc.be.datatypes.elements.MapListRequirementDataDefinition; +import org.openecomp.sdc.be.datatypes.elements.RequirementDataDefinition; import org.openecomp.sdc.be.datatypes.enums.GraphPropertyEnum; import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields; import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum; -import org.openecomp.sdc.be.datatypes.tosca.ToscaDataDefinition; -import org.openecomp.sdc.be.model.*; +import org.openecomp.sdc.be.model.ArtifactDefinition; +import org.openecomp.sdc.be.model.CapabilityDefinition; +import org.openecomp.sdc.be.model.CapabilityRequirementRelationship; +import org.openecomp.sdc.be.model.Component; +import org.openecomp.sdc.be.model.ComponentInstance; +import org.openecomp.sdc.be.model.ComponentInstanceAttribute; +import org.openecomp.sdc.be.model.ComponentInstanceOutput; +import org.openecomp.sdc.be.model.GroupDefinition; +import org.openecomp.sdc.be.model.ModelTestBase; +import org.openecomp.sdc.be.model.RelationshipImpl; +import org.openecomp.sdc.be.model.RelationshipInfo; +import org.openecomp.sdc.be.model.RequirementCapabilityRelDef; +import org.openecomp.sdc.be.model.Resource; +import org.openecomp.sdc.be.model.User; import org.openecomp.sdc.be.model.jsonjanusgraph.datamodel.TopologyTemplate; import org.openecomp.sdc.be.model.jsonjanusgraph.datamodel.ToscaElement; +import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; import org.openecomp.sdc.common.api.ArtifactGroupTypeEnum; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Iterator; -import java.util.List; -import java.util.Map; - -import static org.codehaus.groovy.runtime.DefaultGroovyMethods.any; -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertNotNull; -import static org.junit.jupiter.api.Assertions.assertSame; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.ArgumentMatchers.anyList; -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.doReturn; -import static org.mockito.Mockito.times; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - @ExtendWith(MockitoExtension.class) @TestInstance(Lifecycle.PER_CLASS) class NodeTemplateOperationTest extends ModelTestBase { @@ -205,10 +224,8 @@ class NodeTemplateOperationTest extends ModelTestBase { @Test void testGetDefaultHeatTimeout() { - Integer result; - - // default test - result = NodeTemplateOperation.getDefaultHeatTimeout(); + assertEquals(ConfigurationManager.getConfigurationManager().getConfiguration().getHeatArtifactDeploymentTimeout().getDefaultMinutes(), + NodeTemplateOperation.getDefaultHeatTimeout()); } @Test @@ -234,13 +251,8 @@ class NodeTemplateOperationTest extends ModelTestBase { } @Test - void testCreateCapPropertyKey() throws Exception { - String key = ""; - String instanceId = ""; - String result; - - // default test - result = NodeTemplateOperation.createCapPropertyKey(key, instanceId); + void testCreateCapPropertyKey() { + assertEquals("instanceId#instanceId#key", NodeTemplateOperation.createCapPropertyKey("key", "instanceId")); } @Test @@ -438,6 +450,119 @@ class NodeTemplateOperationTest extends ModelTestBase { eq(componentInstanceOutputList), ArgumentMatchers.anyList(), eq(JsonPresentationFields.NAME)); } + @Test + void updateComponentInstanceCapabilitiesSuccessTest() { + final var capabilityUniqueId = "capabilityUniqueId"; + final var capabilityType = "capabilityType"; + final var componentInstanceId = "componentInstanceId"; + + final var nodeTemplateOperation = spy(operation); + final var capabilityDefinitionDatabase = new CapabilityDataDefinition(); + capabilityDefinitionDatabase.setUniqueId(capabilityUniqueId); + capabilityDefinitionDatabase.setType(capabilityType); + capabilityDefinitionDatabase.setExternal(false); + final var capabilityDefinitionToUpdate = new CapabilityDataDefinition(); + capabilityDefinitionToUpdate.setUniqueId(capabilityUniqueId); + capabilityDefinitionToUpdate.setType(capabilityType); + capabilityDefinitionToUpdate.setExternal(true); + final Map capabilityByInstanceMap = new HashMap<>(); + var mapListCapabilityDataDefinition = new MapListCapabilityDataDefinition(); + mapListCapabilityDataDefinition.add(capabilityDefinitionDatabase.getType(), capabilityDefinitionDatabase); + capabilityByInstanceMap.put(componentInstanceId, mapListCapabilityDataDefinition); + + final GraphVertex componentVertex = Mockito.mock(GraphVertex.class); + final GraphVertex capabilitiesVertex = Mockito.mock(GraphVertex.class); + doReturn(capabilityByInstanceMap).when(capabilitiesVertex).getJson(); + when(janusGraphDao.getVertexById(COMPONENT_ID, JsonParseFlagEnum.ParseAll)).thenReturn(Either.left(componentVertex)); + when(janusGraphDao.getChildVertex(componentVertex, EdgeLabelEnum.CALCULATED_CAPABILITIES, JsonParseFlagEnum.ParseJson)) + .thenReturn(Either.left(capabilitiesVertex)); + when(janusGraphDao.updateVertex(componentVertex)).thenReturn(Either.left(componentVertex)); + doReturn(Either.left(capabilitiesVertex)) + .when(nodeTemplateOperation).updateOrCopyOnUpdate(capabilitiesVertex, componentVertex, EdgeLabelEnum.CALCULATED_CAPABILITIES); + final CapabilityDataDefinition actualCapabilityDataDefinition = nodeTemplateOperation + .updateComponentInstanceCapabilities(COMPONENT_ID, componentInstanceId, capabilityDefinitionToUpdate); + assertTrue(actualCapabilityDataDefinition.isExternal()); + } + + @Test + void updateComponentInstanceCapabilitiesTest_capabilityNotFound() { + final var componentInstanceId = "componentInstanceId"; + var capabilityDataDefinition = new CapabilityDataDefinition(); + capabilityDataDefinition.setType("aCapabilityType"); + final GraphVertex componentVertex = Mockito.mock(GraphVertex.class); + final GraphVertex calculatedCapabilityVertex = Mockito.mock(GraphVertex.class); + //no capabilities found + when(janusGraphDao.getVertexById(COMPONENT_ID, JsonParseFlagEnum.ParseAll)).thenReturn(Either.left(componentVertex)); + when(janusGraphDao.getChildVertex(componentVertex, EdgeLabelEnum.CALCULATED_CAPABILITIES, JsonParseFlagEnum.ParseJson)) + .thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND)); + OperationException actualException = assertThrows(OperationException.class, () -> + operation.updateComponentInstanceCapabilities(COMPONENT_ID, componentInstanceId, capabilityDataDefinition)); + assertEquals(ActionStatus.CAPABILITY_OF_INSTANCE_NOT_FOUND_ON_CONTAINER, actualException.getActionStatus()); + assertEquals(actualException.getParams().length, 3); + assertEquals(capabilityDataDefinition.getName(), actualException.getParams()[0]); + assertEquals(capabilityDataDefinition.getOwnerName(), actualException.getParams()[1]); + assertEquals(COMPONENT_ID, actualException.getParams()[2]); + + //found capabilities, but not for the provided instance id + when(janusGraphDao.getChildVertex(componentVertex, EdgeLabelEnum.CALCULATED_CAPABILITIES, JsonParseFlagEnum.ParseJson)) + .thenReturn(Either.left(calculatedCapabilityVertex)); + actualException = assertThrows(OperationException.class, () -> + operation.updateComponentInstanceCapabilities(COMPONENT_ID, "componentInstanceId", capabilityDataDefinition)); + assertEquals(ActionStatus.CAPABILITY_OF_INSTANCE_NOT_FOUND_ON_CONTAINER, actualException.getActionStatus()); + assertEquals(actualException.getParams().length, 3); + assertEquals(capabilityDataDefinition.getName(), actualException.getParams()[0]); + assertEquals(capabilityDataDefinition.getOwnerName(), actualException.getParams()[1]); + assertEquals(COMPONENT_ID, actualException.getParams()[2]); + + //found capabilities for the instance id, but not with the provided capability type + final Map capabilityByInstanceMap = new HashMap<>(); + var mapListCapabilityDataDefinition = new MapListCapabilityDataDefinition(); + mapListCapabilityDataDefinition.add("anotherCapabilityType", new CapabilityDataDefinition()); + capabilityByInstanceMap.put(componentInstanceId, mapListCapabilityDataDefinition); + doReturn(capabilityByInstanceMap).when(calculatedCapabilityVertex).getJson(); + actualException = assertThrows(OperationException.class, () -> + operation.updateComponentInstanceCapabilities(COMPONENT_ID, "componentInstanceId", capabilityDataDefinition)); + assertEquals(ActionStatus.CAPABILITY_OF_INSTANCE_NOT_FOUND_ON_CONTAINER, actualException.getActionStatus()); + assertEquals(actualException.getParams().length, 3); + assertEquals(capabilityDataDefinition.getName(), actualException.getParams()[0]); + assertEquals(capabilityDataDefinition.getOwnerName(), actualException.getParams()[1]); + assertEquals(COMPONENT_ID, actualException.getParams()[2]); + } + + @Test + void updateComponentInstanceCapabilitiesTest_capabilityFindError() { + final GraphVertex componentVertex = Mockito.mock(GraphVertex.class); + when(componentVertex.getUniqueId()).thenReturn(COMPONENT_ID); + when(janusGraphDao.getVertexById(COMPONENT_ID, JsonParseFlagEnum.ParseAll)).thenReturn(Either.left(componentVertex)); + when(janusGraphDao.getChildVertex(componentVertex, EdgeLabelEnum.CALCULATED_CAPABILITIES, JsonParseFlagEnum.ParseJson)) + .thenReturn(Either.right(JanusGraphOperationStatus.GENERAL_ERROR)); + final OperationException actualException = assertThrows(OperationException.class, () -> + operation.updateComponentInstanceCapabilities(COMPONENT_ID, "componentInstanceId", new CapabilityDataDefinition())); + assertEquals(ActionStatus.COMPONENT_CAPABILITIES_FIND_ERROR, actualException.getActionStatus()); + assertEquals(actualException.getParams().length, 1); + assertEquals(COMPONENT_ID, actualException.getParams()[0]); + } + + @Test + void updateComponentInstanceCapabilitiesTest_componentNotFound() { + when(janusGraphDao.getVertexById(COMPONENT_ID, JsonParseFlagEnum.ParseAll)).thenReturn(Either.right(JanusGraphOperationStatus.NOT_FOUND)); + final OperationException actualException = assertThrows(OperationException.class, () -> + operation.updateComponentInstanceCapabilities(COMPONENT_ID, "componentInstanceId", new CapabilityDataDefinition())); + assertEquals(ActionStatus.COMPONENT_NOT_FOUND, actualException.getActionStatus()); + assertEquals(actualException.getParams().length, 1); + assertEquals(COMPONENT_ID, actualException.getParams()[0]); + } + + @Test + void updateComponentInstanceCapabilitiesTest_componentFindError() { + when(janusGraphDao.getVertexById(COMPONENT_ID, JsonParseFlagEnum.ParseAll)).thenReturn(Either.right(JanusGraphOperationStatus.GENERAL_ERROR)); + final OperationException actualException = assertThrows(OperationException.class, () -> + operation.updateComponentInstanceCapabilities(COMPONENT_ID, "componentInstanceId", new CapabilityDataDefinition())); + assertEquals(ActionStatus.COMPONENT_FIND_ERROR, actualException.getActionStatus()); + assertEquals(actualException.getParams().length, 1); + assertEquals(COMPONENT_ID, actualException.getParams()[0]); + } + private ComponentInstance createCompInstance() { ComponentInstance componentInstance = new ComponentInstance(); String id = "id"; diff --git a/catalog-model/src/test/java/org/openecomp/sdc/be/ui/mapper/CapabilityMapperTest.java b/catalog-model/src/test/java/org/openecomp/sdc/be/ui/mapper/CapabilityMapperTest.java new file mode 100644 index 0000000000..6e147281b8 --- /dev/null +++ b/catalog-model/src/test/java/org/openecomp/sdc/be/ui/mapper/CapabilityMapperTest.java @@ -0,0 +1,60 @@ +/* + * ============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.mapper; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.openecomp.sdc.be.model.CapabilityDefinition; +import org.openecomp.sdc.be.ui.model.ComponentInstanceCapabilityUpdateModel; + +class CapabilityMapperTest { + + private CapabilityMapper capabilityMapper; + + @BeforeEach + void beforeEach() { + capabilityMapper = new CapabilityMapper(); + } + + @Test + void mapToCapabilityDefinitionTest() { + final ComponentInstanceCapabilityUpdateModel capabilityUpdateModel = new ComponentInstanceCapabilityUpdateModel(); + capabilityUpdateModel.setUniqueId("uniqueId"); + capabilityUpdateModel.setName("name"); + capabilityUpdateModel.setExternal(true); + capabilityUpdateModel.setOwnerId("ownerId"); + capabilityUpdateModel.setType("type"); + capabilityUpdateModel.setOwnerName("ownerName"); + final CapabilityDefinition capabilityDefinition = capabilityMapper.mapToCapabilityDefinition(capabilityUpdateModel); + assertCapabilityDefinition(capabilityDefinition, capabilityUpdateModel); + } + + private void assertCapabilityDefinition(final CapabilityDefinition actualCapabilityDefinition, + final ComponentInstanceCapabilityUpdateModel 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.getExternal(), actualCapabilityDefinition.isExternal()); + } +} \ No newline at end of file diff --git a/catalog-ui/src/app/models/capability.ts b/catalog-ui/src/app/models/capability.ts index f365dc4940..b377ed9edd 100644 --- a/catalog-ui/src/app/models/capability.ts +++ b/catalog-ui/src/app/models/capability.ts @@ -62,6 +62,7 @@ export class Capability implements RequirementCapabilityModel { description: string; validSourceTypes: string[]; properties: PropertyModel[]; + external: boolean; // custom selected: boolean; @@ -84,6 +85,7 @@ export class Capability implements RequirementCapabilityModel { this.description = capability.description; this.validSourceTypes = capability.validSourceTypes; this.selected = capability.selected; + this.external = capability.external; this.initFilterTerm(); } diff --git a/catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.ts b/catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.ts index 3cab4b300f..8d2357d6ad 100644 --- a/catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.ts +++ b/catalog-ui/src/app/ng2/pages/composition/graph/composition-graph.component.ts @@ -25,7 +25,8 @@ import { ZoneInstanceAssignmentType, ZoneInstanceMode, ZoneInstanceType, - Requirement + Requirement, + Capability } from 'app/models'; import { ForwardingPath } from 'app/models/forwarding-path'; import { CompositionCiServicePathLink } from 'app/models/graph/graph-links/composition-graph-links/composition-ci-service-path-link'; @@ -649,6 +650,14 @@ export class CompositionGraphComponent implements AfterViewInit { .find(r => r.uniqueId === requirement.uniqueId).external = requirement.external; }); + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_CAPABILITY_EXTERNAL_CHANGED, + (uniqueId: string, capability: Capability) => { + const graphCapability = this._cy.getElementById(uniqueId).data() + .componentInstance.capabilities[capability.type].find(cap => cap.uniqueId === capability.uniqueId); + graphCapability.external = capability.external; + } + ); + this.eventListenerService.registerObserverCallback(GRAPH_EVENTS.ON_DELETE_COMPONENT_INSTANCE, (componentInstanceId: string) => { const nodeToDelete = this._cy.getElementById(componentInstanceId); this.nodesGraphUtils.deleteNode(this._cy, this.topologyTemplate, nodeToDelete); diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/req-capabilities-tab/req-capabilities-tab.component.html b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/req-capabilities-tab/req-capabilities-tab.component.html index c73f69734f..ad25aabefd 100644 --- a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/req-capabilities-tab/req-capabilities-tab.component.html +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/req-capabilities-tab/req-capabilities-tab.component.html @@ -5,6 +5,16 @@
{{capability.name}} 
{{capability.type}}
+
+ + +
@@ -19,10 +29,20 @@ -
-
{{capability.name}} 
-
{{capability.type}}
-
+
+
{{capability.name}} 
+
{{capability.type}}
+
+ + +
+
diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/req-capabilities-tab/req-capabilities-tab.component.ts b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/req-capabilities-tab/req-capabilities-tab.component.ts index 7c91cbc4b8..7bb88c7f59 100644 --- a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/req-capabilities-tab/req-capabilities-tab.component.ts +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/req-capabilities-tab/req-capabilities-tab.component.ts @@ -1,5 +1,5 @@ import { Component, OnInit, Input, OnDestroy } from '@angular/core'; -import { Component as TopologyTemplate, Capability, Requirement, CapabilitiesGroup, RequirementsGroup, ComponentInstance, FullComponentInstance } from "app/models"; +import { Component as TopologyTemplate, Capability, Requirement, CapabilitiesGroup, RequirementsGroup, FullComponentInstance } from "app/models"; import { Store } from "@ngxs/store"; import { GRAPH_EVENTS } from "app/utils"; import { ComponentGenericResponse } from "app/ng2/services/responses/component-generic-response"; @@ -8,6 +8,7 @@ import { EventListenerService } from "app/services"; import { WorkspaceService } from "app/ng2/pages/workspace/workspace.service"; import { CompositionService } from "app/ng2/pages/composition/composition.service"; import {SelectedComponentType, TogglePanelLoadingAction} from "../../../common/store/graph.actions"; +import {ComponentInstanceServiceNg2} from "../../../../../services/component-instance-services/component-instance.service"; export class InstanceCapabilitiesMap { @@ -42,7 +43,8 @@ export class ReqAndCapabilitiesTabComponent implements OnInit, OnDestroy { private topologyTemplateService:TopologyTemplateService, private workspaceService: WorkspaceService, private compositionService: CompositionService, - private eventListenerService:EventListenerService) { } + private eventListenerService:EventListenerService, + private componentInstanceService: ComponentInstanceServiceNg2) { } ngOnInit(): void { @@ -112,13 +114,19 @@ export class ReqAndCapabilitiesTabComponent implements OnInit, OnDestroy { private initInstancesMap = ():void => { this.capabilitiesInstancesMap = new InstanceCapabilitiesMap(); - _.forEach(this.capabilities, (capability:Capability) => { - if (this.capabilitiesInstancesMap[capability.ownerName]) { - this.capabilitiesInstancesMap[capability.ownerName] = this.capabilitiesInstancesMap[capability.ownerName].concat(capability); - } else { - this.capabilitiesInstancesMap[capability.ownerName] = new Array(capability); + let capabilityList: Array = this.capabilities; + if (capabilityList) { + if (!this.isComponentInstanceSelected) { + capabilityList = capabilityList.filter(value => value.external); } - }); + capabilityList.forEach(capability => { + if (this.capabilitiesInstancesMap[capability.ownerName]) { + this.capabilitiesInstancesMap[capability.ownerName] = this.capabilitiesInstancesMap[capability.ownerName].concat(capability); + } else { + this.capabilitiesInstancesMap[capability.ownerName] = new Array(capability); + } + }); + } this.requirementsInstancesMap = new InstanceRequirementsMap(); _.forEach(this.requirements, (requirement:Requirement) => { @@ -161,7 +169,21 @@ export class ReqAndCapabilitiesTabComponent implements OnInit, OnDestroy { } - - + onMarkCapabilityAsExternal(capability: Capability) { + this.store.dispatch(new TogglePanelLoadingAction({isLoading: true})); + capability.external = !capability.external; + const componentId = this.workspaceService.metadata.uniqueId; + const componentInstanceId = this.component.uniqueId; + this.componentInstanceService + .updateInstanceCapability(this.workspaceService.metadata.getTypeUrl(), componentId, componentInstanceId, capability) + .subscribe(() => { + this.eventListenerService.notifyObservers(GRAPH_EVENTS.ON_COMPONENT_INSTANCE_CAPABILITY_EXTERNAL_CHANGED, componentInstanceId, capability); + this.store.dispatch(new TogglePanelLoadingAction({isLoading: false})); + } , (error) => { + console.error("An error has occurred while setting capability '" + capability.name + "' external", error); + capability.external = !capability.external; + this.store.dispatch(new TogglePanelLoadingAction({isLoading: false})); + }); + } } diff --git a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/req-capabilities-tab/requirement-list/requirement-list.component.html b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/req-capabilities-tab/requirement-list/requirement-list.component.html index ebaed0441a..ee8277971b 100644 --- a/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/req-capabilities-tab/requirement-list/requirement-list.component.html +++ b/catalog-ui/src/app/ng2/pages/composition/panel/panel-tabs/req-capabilities-tab/requirement-list/requirement-list.component.html @@ -17,7 +17,7 @@ *ngIf="isInstanceSelected"> { + let httpTestingController: HttpTestingController; + let componentInstanceService: ComponentInstanceServiceNg2; + let rootApi: string = 'http://localhost/' + let componentApiRoot: string = 'catalog/' + beforeEach(() => { + const sdcConfigToken: Partial = { + 'api': { + 'root': rootApi, + 'component_api_root': componentApiRoot, + } + }; + TestBed.configureTestingModule({ + providers: [ComponentInstanceServiceNg2, + {provide: SdcConfigToken, useValue: sdcConfigToken} + ], + imports: [HttpClientTestingModule] + }); + httpTestingController = TestBed.get(HttpTestingController); + componentInstanceService = TestBed.get(ComponentInstanceServiceNg2); + }); + + it('should be created', () => { + expect(componentInstanceService).toBeTruthy(); + }); + + it('updateInstanceCapability call should return the expected data', () => { + const capabilityToUpdate = new Capability(); + capabilityToUpdate.type = "tosca.capabilities.Scalable"; + capabilityToUpdate.name = "capScalable"; + capabilityToUpdate.ownerId = "191f8a83-d362-4db4-af30-75d71a55c959.a822dd1c-3560-47ea-b8a2-f557fed5e186.vfcapreq10"; + capabilityToUpdate.uniqueId = "2047eb3c-de31-4413-a358-8710a3dd2670"; + capabilityToUpdate.external = true; + + const componentTypeUrl = "services/"; + let actualCapability: Capability; + componentInstanceService.updateInstanceCapability(componentTypeUrl, "componentId", "componentInstanceId", capabilityToUpdate) + .subscribe(capability => { + actualCapability = capability; + }); + + const request = + httpTestingController.expectOne(`${rootApi}${componentApiRoot}${componentTypeUrl}componentId/componentInstances/componentInstanceId/capability/`); + + expect(request.request.method).toEqual('PUT'); + + request.flush(capabilityToUpdate); + expect(actualCapability.name).toEqual(capabilityToUpdate.name); + expect(actualCapability.type).toEqual(capabilityToUpdate.type); + expect(actualCapability.ownerId).toEqual(capabilityToUpdate.ownerId); + expect(actualCapability.uniqueId).toEqual(capabilityToUpdate.uniqueId); + expect(actualCapability.external).toEqual(capabilityToUpdate.external); + }); + +}); diff --git a/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts b/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts index 5ae2918805..1e4ddda9c0 100644 --- a/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts +++ b/catalog-ui/src/app/ng2/services/component-instance-services/component-instance.service.ts @@ -18,15 +18,25 @@ * ============LICENSE_END========================================================= */ -import {Injectable, Inject} from '@angular/core'; -import { Observable } from 'rxjs/Observable'; -import {PropertyFEModel, PropertyBEModel, Requirement} from "app/models"; -import {CommonUtils, ComponentType, ServerTypeUrl, ComponentInstanceFactory} from "app/utils"; -import {Component, ComponentInstance, Capability, PropertyModel, ArtifactGroupModel, ArtifactModel, AttributeModel, IFileDownload} from "app/models"; -import {SdcConfigToken, ISdcConfig} from "../../config/sdc-config.config"; -import { HttpClient, HttpHeaders } from '@angular/common/http'; -import { InputBEModel } from '../../../models/properties-inputs/input-be-model'; -import { HttpHelperService } from '../http-hepler.service'; +import {Inject, Injectable} from '@angular/core'; +import {Observable} from 'rxjs/Observable'; +import { + ArtifactGroupModel, + ArtifactModel, + AttributeModel, + Capability, + Component, + ComponentInstance, + IFileDownload, + PropertyBEModel, + PropertyModel, + Requirement +} from "app/models"; +import {CommonUtils, ComponentInstanceFactory, ComponentType, ServerTypeUrl} from "app/utils"; +import {ISdcConfig, SdcConfigToken} from "../../config/sdc-config.config"; +import {HttpClient, HttpHeaders} from '@angular/common/http'; +import {InputBEModel} from '../../../models/properties-inputs/input-be-model'; +import {HttpHelperService} from '../http-hepler.service'; import {AttributeBEModel} from "../../../models/attributes-outputs/attribute-be-model"; import {OutputBEModel} from "../../../models/attributes-outputs/output-be-model"; @@ -158,6 +168,11 @@ export class ComponentInstanceServiceNg2 { '/requirementName/' + requirement.name, requirement); } + updateInstanceCapability(componentTypeUrl: string, componentId: string, componentInstanceId: string, capability: Capability): Observable { + const url = `${this.baseUrl}${componentTypeUrl}${componentId}/componentInstances/${componentInstanceId}/capability/`; + return this.http.put(url, capability); + } + updateInstanceInputs(component: Component, componentInstanceId: string, inputs: PropertyBEModel[]): Observable { return this.http.post>(this.baseUrl + component.getTypeUrl() + component.uniqueId + '/resourceInstance/' + componentInstanceId + '/inputs', inputs) @@ -203,7 +218,6 @@ export class ComponentInstanceServiceNg2 { } addInstanceArtifact = (componentType:string, componentId:string, instanceId:string, artifact:ArtifactModel):Observable => { - // let deferred = this.$q.defer(); let headerObj = new HttpHeaders(); if (artifact.payloadData) { headerObj = headerObj.set('Content-MD5', HttpHelperService.getHeaderMd5(artifact)); @@ -217,7 +231,7 @@ export class ComponentInstanceServiceNg2 { updateInstanceArtifact = (componentType:string, componentId:string, instanceId:string, artifact:ArtifactModel):Observable => { return this.http.post(this.baseUrl + this.getServerTypeUrl(componentType) + componentId + '/resourceInstance/' + instanceId + '/artifacts/' + artifact.uniqueId, artifact).map((res) => { return new ArtifactModel(res); - });; + }); }; deleteInstanceArtifact = (componentId:string, componentType:string, instanceId:string, artifactId:string, artifactLabel:string):Observable => { diff --git a/catalog-ui/src/app/utils/constants.ts b/catalog-ui/src/app/utils/constants.ts index 3e86ec9e96..f7cbf8d7b0 100644 --- a/catalog-ui/src/app/utils/constants.ts +++ b/catalog-ui/src/app/utils/constants.ts @@ -349,6 +349,7 @@ export class GRAPH_EVENTS { static ON_PALETTE_COMPONENT_HIDE_POPUP_PANEL = 'onPaletteComponentHidePopupPanel'; static ON_COMPONENT_INSTANCE_NAME_CHANGED = 'onComponentInstanceNameChanged'; static ON_COMPONENT_INSTANCE_REQUIREMENT_EXTERNAL_CHANGED = 'onComponentInstanceRequirementExternalChanged' + static ON_COMPONENT_INSTANCE_CAPABILITY_EXTERNAL_CHANGED = 'onComponentInstanceCapabilityExternalChanged' static ON_ZONE_INSTANCE_NAME_CHANGED = 'onZoneInstanceNameChanged'; static ON_DELETE_COMPONENT_INSTANCE = 'onDeleteComponentInstance'; static ON_DELETE_ZONE_INSTANCE = 'onDeleteZoneInstance'; diff --git a/common-app-api/src/main/java/org/openecomp/sdc/exception/ResponseFormat.java b/common-app-api/src/main/java/org/openecomp/sdc/exception/ResponseFormat.java index b0467425ec..2840ccd3b4 100644 --- a/common-app-api/src/main/java/org/openecomp/sdc/exception/ResponseFormat.java +++ b/common-app-api/src/main/java/org/openecomp/sdc/exception/ResponseFormat.java @@ -19,6 +19,8 @@ */ package org.openecomp.sdc.exception; +import lombok.Setter; + /** * Nested POJOs to express required JSON format of the error *

@@ -28,6 +30,7 @@ package org.openecomp.sdc.exception; */ public class ResponseFormat { + @Setter private int status; private RequestErrorWrapper requestErrorWrapper; @@ -40,10 +43,6 @@ public class ResponseFormat { this.status = status; } - public void setStatus(int status) { - this.status = status; - } - public Integer getStatus() { return status; } @@ -153,9 +152,6 @@ public class ResponseFormat { @SuppressWarnings("unused") private OkResponseInfo okResponseInfo; - public RequestError() { - } - public PolicyException getPolicyException() { return policyException; } diff --git a/common-app-logging/src/main/java/org/openecomp/sdc/common/log/enums/LoggerSupportabilityActions.java b/common-app-logging/src/main/java/org/openecomp/sdc/common/log/enums/LoggerSupportabilityActions.java index 88b6dbfb6a..253a41f8cb 100644 --- a/common-app-logging/src/main/java/org/openecomp/sdc/common/log/enums/LoggerSupportabilityActions.java +++ b/common-app-logging/src/main/java/org/openecomp/sdc/common/log/enums/LoggerSupportabilityActions.java @@ -79,6 +79,7 @@ public enum LoggerSupportabilityActions { UPDATE_GROUP_MEMBERS("UPDATE GROUP MEMBERS"), UPDATE_INSTANCE_CAPABILITY_PROPERTY("UPDATE INSTANCE CAPABILITY PROPERTY"), UPDATE_INSTANCE_REQUIREMENT("UPDATE INSTANCE REQUIREMENT"), + UPDATE_INSTANCE_CAPABILITY("UPDATE INSTANCE CAPABILITY"), UPDATE_POLICY_TARGET("UPDATE POLICY TARGET"), UPDATE_POLICIES_PROPERTIES("UPDATE POLICIES PROPERTIES"); // @formatter:on diff --git a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/CapabilityDataDefinition.java b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/CapabilityDataDefinition.java index 78bfe50d5c..3345c95946 100644 --- a/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/CapabilityDataDefinition.java +++ b/common-be/src/main/java/org/openecomp/sdc/be/datatypes/elements/CapabilityDataDefinition.java @@ -21,6 +21,8 @@ package org.openecomp.sdc.be.datatypes.elements; import com.google.common.collect.Lists; +import lombok.Getter; +import lombok.Setter; import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields; import org.openecomp.sdc.be.datatypes.tosca.ToscaDataDefinition; @@ -35,6 +37,10 @@ public class CapabilityDataDefinition extends ToscaDataDefinition { public static final String MIN_OCCURRENCES = "0"; public static final String MAX_OCCURRENCES = "UNBOUNDED"; + @Getter + @Setter + private boolean external = false; + /** * The default constructor initializing limits of the occurrences */ @@ -45,11 +51,11 @@ public class CapabilityDataDefinition extends ToscaDataDefinition { } /** - * Deep copy constructor + * Deep copy constructor. * - * @param other + * @param other the capability data definition to copy */ - public CapabilityDataDefinition(CapabilityDataDefinition other) { + public CapabilityDataDefinition(final CapabilityDataDefinition other) { this.setUniqueId(other.getUniqueId()); this.setType(other.getType()); this.setDescription(other.getDescription()); @@ -84,6 +90,7 @@ public class CapabilityDataDefinition extends ToscaDataDefinition { this.setSource(other.getSource()); this.setOwnerType(other.getOwnerType()); + this.setExternal(other.isExternal()); } public CapabilityDataDefinition(CapabilityTypeDataDefinition other) { @@ -422,11 +429,11 @@ public class CapabilityDataDefinition extends ToscaDataDefinition { String maxOccurrences = this.getMaxOccurrences(); String source = this.getSource(); - return "CapabilityDefinition [uniqueId=" + uniqueId + ", description=" + description + ", name=" + name - + ", type=" + type + ", validSourceTypes=" + validSourceTypes + ", capabilitySources=" - + capabilitySources + ", ownerId=" + ownerId + ", ownerName=" + ownerName - + ", minOccurrences=" + minOccurrences + ", maxOccurrences=" + maxOccurrences + ", path=" + path + ", source=" + source + "]"; + + ", type=" + type + ", validSourceTypes=" + validSourceTypes + ", capabilitySources=" + + capabilitySources + ", ownerId=" + ownerId + ", ownerName=" + ownerName + + ", minOccurrences=" + minOccurrences + ", maxOccurrences=" + maxOccurrences + ", path=" + path + ", source=" + source + + ", external=" + external + "]"; } public enum OwnerType { diff --git a/common-be/src/main/java/org/openecomp/sdc/be/exception/BusinessException.java b/common-be/src/main/java/org/openecomp/sdc/be/exception/BusinessException.java new file mode 100644 index 0000000000..69fb00f3b5 --- /dev/null +++ b/common-be/src/main/java/org/openecomp/sdc/be/exception/BusinessException.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.exception; + +public abstract class BusinessException extends RuntimeException { + + protected BusinessException() { + } + + protected BusinessException(final String message) { + super(message); + } + + protected BusinessException(final String message, final Throwable cause) { + super(message, cause); + } +} diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/CompositionRequirementsCapabilitiesTab.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/CompositionRequirementsCapabilitiesTab.java index bee2d3a339..2ea7437416 100644 --- a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/CompositionRequirementsCapabilitiesTab.java +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/component/workspace/CompositionRequirementsCapabilitiesTab.java @@ -67,11 +67,11 @@ public class CompositionRequirementsCapabilitiesTab extends AbstractPageObject { } private void loadRequirements() { - final List webElements = waitForAllElementsVisibility(By.xpath("//checkbox[@data-tests-id]")); + final List webElements = waitForAllElementsVisibility(By.xpath(XpathSelector.REQUIREMENT_EXTERNAL_CHECKBOX.getXPath())); checkboxExternalRequirementMap = new HashMap<>(); webElements.forEach(webElement -> { final String dataTestsId = webElement.getAttribute("data-tests-id"); - checkboxExternalRequirementMap.put(dataTestsId.substring("checkbox-mark-as-external-".length()), webElement); + checkboxExternalRequirementMap.put(dataTestsId.substring("checkbox-external-req-".length()), webElement); }); } @@ -94,7 +94,8 @@ public class CompositionRequirementsCapabilitiesTab extends AbstractPageObject { private enum XpathSelector { REQ_CAPABILITIES_TAB("//req-capabilities-tab"), CAPABILITIES_ACCORDION("//div[@data-tests-id='Capabilities-accordion']"), - REQUIREMENTS_ACCORDION("//div[@data-tests-id='Requirements-accordion']"); + REQUIREMENTS_ACCORDION("//div[@data-tests-id='Requirements-accordion']"), + REQUIREMENT_EXTERNAL_CHECKBOX("//checkbox[starts-with(@data-tests-id, 'checkbox-external-req-')]"); private final String xPath; -- cgit 1.2.3-korg