From 6047cd212696f5260d1296ce1fc3449dadb6005d Mon Sep 17 00:00:00 2001 From: aribeiro Date: Tue, 18 May 2021 20:57:07 +0100 Subject: Support for associating node types to models Issue-ID: SDC-3597 Signed-off-by: aribeiro Signed-off-by: MichaelMorris Change-Id: Icd0066240b78ba98d8f0efab66d11756f18cb251 --- .../files/default/error-configuration.yaml | 24 ++ .../impl/ComponentInstanceBusinessLogic.java | 6 +- .../sdc/be/components/impl/ModelBusinessLogic.java | 4 + .../be/components/impl/ResourceBusinessLogic.java | 28 +- .../be/components/impl/ResourceImportManager.java | 46 +++- .../be/components/impl/ServiceBusinessLogic.java | 2 +- .../impl/exceptions/UploadResourceException.java | 30 +++ .../sdc/be/servlets/ResourceUploadServlet.java | 59 +++-- .../openecomp/sdc/be/tosca/ToscaExportHandler.java | 3 +- .../sdc/be/tosca/model/ToscaMetadata.java | 9 + .../be/components/ResourceImportManagerTest.java | 13 +- .../components/impl/ResourceBusinessLogicTest.java | 69 ++++- .../beans/ForwardingPathToscaOperationFacade.java | 5 +- .../be/components/path/utils/GraphTestUtils.java | 1 + .../sdc/be/servlets/ArchiveEndpointTest.java | 6 + .../sdc/be/servlets/ResourceUploadServletTest.java | 289 +++++++++++++++++++++ .../sdc/be/tosca/ToscaExportHandlerTest.java | 8 +- .../test/resources/node-types/TestNodeType001.yml | 18 ++ .../test/resources/node-types/TestNodeType001.zip | Bin 0 -> 558 bytes .../test/resources/node-types/TestNodeType002.zip | Bin 0 -> 559 bytes .../src/test/resources/node-types/invalid.json | 3 + .../node-types/nodeTypeWithEmptyModels.json | 16 ++ .../node-types/nodeTypeWithModelsField.json | 16 ++ .../node-types/nodeTypeWithoutModelsField.json | 15 ++ 24 files changed, 600 insertions(+), 70 deletions(-) create mode 100644 catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/exceptions/UploadResourceException.java create mode 100644 catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ResourceUploadServletTest.java create mode 100644 catalog-be/src/test/resources/node-types/TestNodeType001.yml create mode 100644 catalog-be/src/test/resources/node-types/TestNodeType001.zip create mode 100644 catalog-be/src/test/resources/node-types/TestNodeType002.zip create mode 100644 catalog-be/src/test/resources/node-types/invalid.json create mode 100644 catalog-be/src/test/resources/node-types/nodeTypeWithEmptyModels.json create mode 100644 catalog-be/src/test/resources/node-types/nodeTypeWithModelsField.json create mode 100644 catalog-be/src/test/resources/node-types/nodeTypeWithoutModelsField.json (limited to 'catalog-be/src') diff --git a/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml b/catalog-be/src/main/docker/backend/chef-repo/cookbooks/sdc-catalog-be/files/default/error-configuration.yaml index b277aeef2f..c87e1e394b 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 @@ -2495,3 +2495,27 @@ errors: message: "Could not read imports zip.", messageId: "SVC4147" } + + #---------SVC4148------------------------------ + # %1 - "Model name" + MODEL_NOT_FOUND: { + code: 404, + message: "Error: Model name '%1' not found. Please, make sure the model is created.", + messageId: "SVC4148" + } + + #---------SVC4149------------------------------ + MODEL_NAME_CANNOT_BE_EMPTY: { + code: 409, + message: "Error: Model name cannot be empty.", + messageId: "SVC4149" + } + + #-----------SVC4150--------------------------- + # %1 - "Component name" + # %2 - "Model name" + COMPONENT_WITH_MODEL_ALREADY_EXIST: { + code: 409, + message: "Error: Component %1 with Model %2 already exist.", + messageId: "SVC4150" + } 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 2e54ff7adb..bce343efaf 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 @@ -425,7 +425,7 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic { } private Component getOrigComponentForServiceProxy(org.openecomp.sdc.be.model.Component containerComponent, ComponentInstance resourceInstance) { - Either serviceProxyOrigin = toscaOperationFacade.getLatestByName(SERVICE_PROXY); + Either serviceProxyOrigin = toscaOperationFacade.getLatestByName(SERVICE_PROXY, null); if (isServiceProxyOrigin(serviceProxyOrigin)) { throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(serviceProxyOrigin.right().value())); } @@ -527,7 +527,7 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic { validateInstanceName(resourceInstance); if (originType == OriginTypeEnum.ServiceProxy) { log.debug("enter createRealComponentInstance,originType equals ServiceProxy"); - Either serviceProxyOrigin = toscaOperationFacade.getLatestByName(SERVICE_PROXY); + Either serviceProxyOrigin = toscaOperationFacade.getLatestByName(SERVICE_PROXY, null); if (isServiceProxyOrigin(serviceProxyOrigin)) { throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(serviceProxyOrigin.right().value())); } @@ -2839,7 +2839,7 @@ public class ComponentInstanceBusinessLogic extends BaseBusinessLogic { newComponentInstance.setOriginType(originType); if (originType == OriginTypeEnum.ServiceProxy) { Either serviceProxyOrigin = toscaOperationFacade - .getLatestByName(SERVICE_PROXY); + .getLatestByName(SERVICE_PROXY, null); if (isServiceProxyOrigin(serviceProxyOrigin)) { throw new ByActionStatusComponentException( componentsUtils.convertFromStorageResponse(serviceProxyOrigin.right().value())); diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogic.java index a048af4ac8..6eb806d3d1 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogic.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ModelBusinessLogic.java @@ -34,6 +34,10 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; + +/** + * This class is responsible for handling the business logic of a Model. + */ @Component("modelBusinessLogic") public class ModelBusinessLogic { diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogic.java index 99ceb2158e..f5b890e438 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogic.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogic.java @@ -19,7 +19,6 @@ */ package org.openecomp.sdc.be.components.impl; -import static java.util.stream.Collectors.joining; import static java.util.stream.Collectors.toList; import static java.util.stream.Collectors.toMap; import static java.util.stream.Collectors.toSet; @@ -44,6 +43,7 @@ import java.util.List; import java.util.ListIterator; import java.util.Map; import java.util.Map.Entry; +import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.function.Function; @@ -1314,6 +1314,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { resourceMetaData.setContactId(user.getUserId()); resourceMetaData.setVendorName(resourceVf.getVendorName()); resourceMetaData.setVendorRelease(resourceVf.getVendorRelease()); + resourceMetaData.setModel(resourceVf.getModel()); // Setting tag final List tags = new ArrayList<>(); tags.add(resourceMetaData.getName()); @@ -1346,6 +1347,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { cvfc.setCreatorUserId(csarInfo.getModifier().getUserId()); cvfc.setVendorName(resourceVf.getVendorName()); cvfc.setVendorRelease(resourceVf.getVendorRelease()); + cvfc.setModel(resourceVf.getModel()); cvfc.setResourceVendorModelNumber(resourceVf.getResourceVendorModelNumber()); cvfc.setToscaResourceName(buildNestedToscaResourceName(ResourceTypeEnum.CVFC.name(), csarInfo.getVfResourceName(), nodeName).getLeft()); cvfc.setInvariantUUID(UniqueIdBuilder.buildInvariantUUID()); @@ -1892,7 +1894,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { throw e; } } - + private boolean nodeTypeAlreadyExists(final String toscaResourceName) { return toscaOperationFacade.getLatestByToscaResourceName(toscaResourceName).isLeft(); } @@ -3277,10 +3279,11 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { ImmutablePair result = null; // check if resource already exists (search by tosca name = type) final boolean isNestedResource = isNestedResourceUpdate(csarInfo, nodeName); + final String resourceName = resource.getToscaResourceName(); final Either latestByToscaName = toscaOperationFacade - .getLatestByToscaResourceName(resource.getToscaResourceName()); - if (latestByToscaName.isLeft()) { - Resource foundResource = latestByToscaName.left().value(); + .getLatestByToscaResourceNameAndModel(resourceName, resource.getModel()); + if (latestByToscaName.isLeft() && Objects.nonNull(latestByToscaName.left().value())) { + final Resource foundResource = latestByToscaName.left().value(); // we don't allow updating names of top level types if (!isNestedResource && !StringUtils.equals(resource.getName(), foundResource.getName())) { BeEcompErrorManager.getInstance() @@ -3349,7 +3352,7 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { } public boolean isResourceExist(String resourceName) { - Either latestByName = toscaOperationFacade.getLatestByName(resourceName); + Either latestByName = toscaOperationFacade.getLatestByName(resourceName, null); return latestByName.isLeft(); } @@ -3464,6 +3467,9 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { if (newResource.getResourceVendorModelNumber() == null) { newResource.setResourceVendorModelNumber(oldResource.getResourceVendorModelNumber()); } + if (newResource.getModel() == null) { + newResource.setModel(oldResource.getModel()); + } if (newResource.getContactId() == null) { newResource.setContactId(oldResource.getContactId()); } @@ -3749,10 +3755,12 @@ public class ResourceBusinessLogic extends ComponentBusinessLogic { } private Resource createResourceTransaction(Resource resource, User user, boolean isNormative) { - // validate resource name uniqueness - log.debug("validate resource name"); - Either eitherValidation = toscaOperationFacade - .validateComponentNameExists(resource.getName(), resource.getResourceType(), resource.getComponentType()); + final String resourceName = resource.getName(); + final String modelName = resource.getModel(); + final ResourceTypeEnum resourceType = resource.getResourceType(); + final ComponentTypeEnum componentType = resource.getComponentType(); + final Either eitherValidation = toscaOperationFacade + .validateComponentNameAndModelExists(resourceName, modelName, resourceType, componentType); if (eitherValidation.isRight()) { loggerSupportability.log(LoggerSupportabilityActions.VALIDATE_NAME, resource.getComponentMetadataForSupportLog(), StatusCode.ERROR, "ERROR while validate component name {} Status is: {}", resource.getName(), eitherValidation.right().value()); diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceImportManager.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceImportManager.java index b1841e55d8..77d8f897e7 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceImportManager.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ResourceImportManager.java @@ -171,19 +171,7 @@ public class ResourceImportManager { setMetaDataFromJson(resourceMetaData, resource); populateResourceFromYaml(resourceYml, resource); validationFunction.apply(resource); - if (!createNewVersion) { - Either latestByName = toscaOperationFacade.getLatestByName(resource.getName()); - if (latestByName.isLeft()) { - throw new ByActionStatusComponentException(ActionStatus.COMPONENT_NAME_ALREADY_EXIST, resource.getName()); - } - } else if (!isCsarPresent(csarInfo)) { - final Either component = toscaOperationFacade - .getComponentByNameAndVendorRelease(resource.getComponentType(), resource.getName(), resource.getVendorRelease(), - JsonParseFlagEnum.ParseAll); - if (component.isLeft()) { - throw new ByActionStatusComponentException(ActionStatus.COMPONENT_VERSION_ALREADY_EXIST, resource.getName()); - } - } + checkResourceExistsBeforeCreate(createNewVersion, csarInfo, resource); resource = resourceBusinessLogic .createOrUpdateResourceByImport(resource, creator, true, isInTransaction, needLock, csarInfo, nodeName, isNested).left; Resource changeStateResponse; @@ -210,6 +198,35 @@ public class ResourceImportManager { return responsePair; } + private void checkResourceExistsBeforeCreate(final boolean createNewVersion, final CsarInfo csarInfo, final Resource resource) { + final String resourceName = resource.getName(); + final String model = resource.getModel(); + final Either latestByToscaName = toscaOperationFacade + .getLatestByToscaResourceNameAndModel(resourceName, model); + if (latestByToscaName.isLeft()) { + final Resource foundResource = latestByToscaName.left().value(); + validateComponentWithModelExist(resourceName, model, foundResource); + if (!createNewVersion) { + throw new ByActionStatusComponentException(ActionStatus.COMPONENT_NAME_ALREADY_EXIST, resourceName); + } + if (!isCsarPresent(csarInfo)) { + final Either component = toscaOperationFacade + .getComponentByNameAndVendorRelease(resource.getComponentType(), resource.getName(), resource.getVendorRelease(), + JsonParseFlagEnum.ParseAll); + if (component.isLeft()) { + validateComponentWithModelExist(resourceName, model, foundResource); + throw new ByActionStatusComponentException(ActionStatus.COMPONENT_VERSION_ALREADY_EXIST, resource.getName()); + } + } + } + } + + private void validateComponentWithModelExist(final String resourceName, final String model, final Resource foundResource) { + if (model != null && toscaOperationFacade.isNodeAssociatedToModel(model, foundResource).isPresent()) { + throw new ByActionStatusComponentException(ActionStatus.COMPONENT_WITH_MODEL_ALREADY_EXIST, resourceName, model); + } + } + private boolean isCsarPresent(final CsarInfo csarInfo) { return csarInfo != null && StringUtils.isNotEmpty(csarInfo.getCsarUUID()); } @@ -246,6 +263,9 @@ public class ResourceImportManager { if (resourceMetaData.getVendorRelease() != null) { resource.setVendorRelease(resourceMetaData.getVendorRelease()); } + if (resourceMetaData.getModel() != null) { + resource.setModel(resourceMetaData.getModel()); + } } } diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceBusinessLogic.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceBusinessLogic.java index a87fdb6ca7..ccaadbad76 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceBusinessLogic.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/ServiceBusinessLogic.java @@ -994,7 +994,7 @@ public class ServiceBusinessLogic extends ComponentBusinessLogic { private Component getForwardingPathOriginComponent() { Either forwardingPathOrigin = toscaOperationFacade - .getLatestByName(ForwardingPathUtils.FORWARDING_PATH_NODE_NAME); + .getLatestByName(ForwardingPathUtils.FORWARDING_PATH_NODE_NAME, null); if (forwardingPathOrigin.isRight()) { StorageOperationStatus errorStatus = forwardingPathOrigin.right().value(); log.debug("Failed to fetch normative forwarding path resource by tosca name, error {}", errorStatus); diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/exceptions/UploadResourceException.java b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/exceptions/UploadResourceException.java new file mode 100644 index 0000000000..de7a47c85c --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/components/impl/exceptions/UploadResourceException.java @@ -0,0 +1,30 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.openecomp.sdc.be.components.impl.exceptions; + +import lombok.Data; +import org.openecomp.sdc.be.exception.BusinessException; +import org.openecomp.sdc.exception.ResponseFormat; + +@Data +public class UploadResourceException extends BusinessException { + + private final ResponseFormat responseFormat; + +} diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourceUploadServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourceUploadServlet.java index 9cf8f67089..bdc9ffce43 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourceUploadServlet.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/servlets/ResourceUploadServlet.java @@ -27,11 +27,8 @@ 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.servers.Servers; import io.swagger.v3.oas.annotations.tags.Tag; -import io.swagger.v3.oas.annotations.tags.Tags; import java.io.File; -import java.io.FileNotFoundException; import javax.inject.Inject; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; @@ -48,19 +45,23 @@ import javax.ws.rs.core.Response; import org.glassfish.jersey.media.multipart.FormDataContentDisposition; import org.glassfish.jersey.media.multipart.FormDataParam; import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic; +import org.openecomp.sdc.be.components.impl.ModelBusinessLogic; import org.openecomp.sdc.be.components.impl.ResourceImportManager; 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.exception.BusinessException; import org.openecomp.sdc.be.impl.ComponentsUtils; import org.openecomp.sdc.be.impl.ServletUtils; import org.openecomp.sdc.be.model.UploadResourceInfo; import org.openecomp.sdc.be.model.User; +import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.ModelOperationExceptionSupplier; import org.openecomp.sdc.be.user.UserBusinessLogic; import org.openecomp.sdc.common.api.Constants; import org.openecomp.sdc.common.datastructure.Wrapper; -import org.openecomp.sdc.common.log.wrappers.Logger; -import org.openecomp.sdc.common.zip.exception.ZipException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; /** @@ -68,8 +69,8 @@ import org.springframework.stereotype.Controller; */ @Loggable(prepend = true, value = Loggable.DEBUG, trim = false) @Path("/v1/catalog/upload") -@Tags({@Tag(name = "SDCE-2 APIs")}) -@Servers({@Server(url = "/sdc2/rest")}) +@Tag(name = "SDCE-2 APIs") +@Server(url = "/sdc2/rest") @Controller public class ResourceUploadServlet extends AbstractValidationsServlet { @@ -77,12 +78,16 @@ public class ResourceUploadServlet extends AbstractValidationsServlet { public static final String CSAR_TYPE_RESOURCE = "csar"; public static final String USER_TYPE_RESOURCE = "user-resource"; public static final String USER_TYPE_RESOURCE_UI_IMPORT = "user-resource-ui-import"; - private static final Logger log = Logger.getLogger(ResourceUploadServlet.class); + private static final Logger log = LoggerFactory.getLogger(ResourceUploadServlet.class); + + private ModelBusinessLogic modelBusinessLogic; @Inject public ResourceUploadServlet(UserBusinessLogic userBusinessLogic, ComponentInstanceBusinessLogic componentInstanceBL, - ComponentsUtils componentsUtils, ServletUtils servletUtils, ResourceImportManager resourceImportManager) { + ComponentsUtils componentsUtils, ServletUtils servletUtils, ResourceImportManager resourceImportManager, + ModelBusinessLogic modelBusinessLogic) { super(userBusinessLogic, componentInstanceBL, componentsUtils, servletUtils, resourceImportManager); + this.modelBusinessLogic = modelBusinessLogic; } @POST @@ -103,8 +108,8 @@ public class ResourceUploadServlet extends AbstractValidationsServlet { @Parameter(description = "ContentDisposition") @FormDataParam("resourceZip") FormDataContentDisposition contentDispositionHeader, @Parameter(description = "resourceMetadata") @FormDataParam("resourceMetadata") String resourceInfoJsonString, @Context final HttpServletRequest request, @HeaderParam(value = Constants.USER_ID_HEADER) String userId, - // updateResourse Query Parameter if false checks if already exist - @DefaultValue("true") @QueryParam("createNewVersion") boolean createNewVersion) throws FileNotFoundException, ZipException { + // updateResource Query Parameter if false checks if already exist + @DefaultValue("true") @QueryParam("createNewVersion") boolean createNewVersion) { try { Wrapper responseWrapper = new Wrapper<>(); Wrapper userWrapper = new Wrapper<>(); @@ -112,12 +117,14 @@ public class ResourceUploadServlet extends AbstractValidationsServlet { Wrapper yamlStringWrapper = new Wrapper<>(); String url = request.getMethod() + " " + request.getRequestURI(); log.debug("Start handle request of {}", url); - // When we get an errorResponse it will be filled into the - - // responseWrapper + // When we get an errorResponse it will be filled into the responseWrapper validateAuthorityType(responseWrapper, resourceAuthority); ResourceAuthorityTypeEnum resourceAuthorityEnum = ResourceAuthorityTypeEnum.findByUrlPath(resourceAuthority); commonGeneralValidations(responseWrapper, userWrapper, uploadResourceInfoWrapper, resourceAuthorityEnum, userId, resourceInfoJsonString); + final String modelNameToBeAssociated = uploadResourceInfoWrapper.getInnerElement().getModel(); + if (modelNameToBeAssociated != null) { + validateModel(modelNameToBeAssociated); + } fillPayload(responseWrapper, uploadResourceInfoWrapper, yamlStringWrapper, userWrapper.getInnerElement(), resourceInfoJsonString, resourceAuthorityEnum, file); // PayLoad Validations @@ -132,10 +139,23 @@ public class ResourceUploadServlet extends AbstractValidationsServlet { yamlStringWrapper.getInnerElement(), resourceAuthorityEnum, createNewVersion, null); } return responseWrapper.getInnerElement(); - } catch (Exception e) { - BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Upload Resource"); - log.debug("upload resource failed with exception", e); + } catch (final BusinessException e) { throw e; + } catch (final Exception e) { + var errorMsg = String.format("Unexpected error while uploading Resource '%s'", resourceInfoJsonString); + BeEcompErrorManager.getInstance().logBeRestApiGeneralError(errorMsg); + log.error(errorMsg, e); + return buildErrorResponse(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)); + } + } + + /** + * The Model field is an optional entry when uploading a resource. If the field is present, it validates if the Model name exists. + * @param modelName Model names declared on the resource json representation + */ + private void validateModel(final String modelName) { + if (modelBusinessLogic.findModel(modelName).isEmpty()) { + throw ModelOperationExceptionSupplier.invalidModel(modelName).get(); } } @@ -148,7 +168,8 @@ public class ResourceUploadServlet extends AbstractValidationsServlet { // @formatter:on private String urlPath; - private boolean isBackEndImport, isUserTypeResource; + private boolean isBackEndImport; + private boolean isUserTypeResource; public static ResourceAuthorityTypeEnum findByUrlPath(String urlPath) { ResourceAuthorityTypeEnum found = null; @@ -161,7 +182,7 @@ public class ResourceUploadServlet extends AbstractValidationsServlet { return found; } - private ResourceAuthorityTypeEnum(String urlPath, boolean isBackEndImport, boolean isUserTypeResource) { + ResourceAuthorityTypeEnum(String urlPath, boolean isBackEndImport, boolean isUserTypeResource) { this.urlPath = urlPath; this.isBackEndImport = isBackEndImport; this.isUserTypeResource = isUserTypeResource; diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java index f193510485..52ced65996 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/ToscaExportHandler.java @@ -472,6 +472,7 @@ public class ToscaExportHandler { toscaMetadata.put(JsonPresentationFields.RESOURCE_VENDOR.getPresentation(), resource.getVendorName()); toscaMetadata.put(JsonPresentationFields.RESOURCE_VENDOR_RELEASE.getPresentation(), resource.getVendorRelease()); toscaMetadata.put(JsonPresentationFields.RESOURCE_VENDOR_MODEL_NUMBER.getPresentation(), resource.getResourceVendorModelNumber()); + toscaMetadata.put(JsonPresentationFields.MODEL.getPresentation(), resource.getModel()); break; case SERVICE: Service service = (Service) component; @@ -1122,7 +1123,7 @@ public class ToscaExportHandler { return res; } Either serviceProxyOrigin = toscaOperationFacade - .getLatestByName("serviceProxy"); + .getLatestByName("serviceProxy", null); if (serviceProxyOrigin.isRight()) { log.debug("Failed to fetch normative service proxy resource by tosca name, error {}", serviceProxyOrigin.right().value()); diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaMetadata.java b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaMetadata.java index 97dab3649e..54d382ff82 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaMetadata.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/tosca/model/ToscaMetadata.java @@ -44,6 +44,7 @@ public class ToscaMetadata implements IToscaMetadata { private String sourceModelName; private String sourceModelUuid; private String serviceFunction; + private String model; public String getName() { return name; @@ -232,4 +233,12 @@ public class ToscaMetadata implements IToscaMetadata { public void setEnvironmentContext(String environmentContext) { this.environmentContext = environmentContext; } + + public String getModel() { + return model; + } + + public void setModel(String model) { + this.model = model; + } } diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceImportManagerTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceImportManagerTest.java index 622027df30..f56ff2e5b9 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceImportManagerTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/ResourceImportManagerTest.java @@ -28,9 +28,9 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; -import static org.mockito.Mockito.when; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; import fj.data.Either; import java.io.IOException; @@ -38,6 +38,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; import org.junit.Before; import org.junit.BeforeClass; @@ -57,7 +58,6 @@ import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction; import org.openecomp.sdc.be.config.Configuration; 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.types.JsonParseFlagEnum; import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition; import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum; @@ -106,6 +106,7 @@ public class ResourceImportManagerTest { importManager = new ResourceImportManager(componentsUtils, capabilityTypeOperation, interfaceDefinitionHandler); importManager.setAuditingManager(auditingManager); when(toscaOperationFacade.getLatestByToscaResourceName(Mockito.anyString())).thenReturn(Either.left(null)); + when(toscaOperationFacade.getLatestByToscaResourceNameAndModel(Mockito.anyString(), Mockito.any())).thenReturn(Either.left(null)); importManager.setResponseFormatManager(responseFormatManager); importManager.setResourceBusinessLogic(resourceBusinessLogic); importManager.setToscaOperationFacade(toscaOperationFacade); @@ -123,7 +124,8 @@ public class ResourceImportManagerTest { public void beforeTest() { Mockito.reset(auditingManager, responseFormatManager, resourceBusinessLogic, userAdmin); Either notFound = Either.right(StorageOperationStatus.NOT_FOUND); - when(toscaOperationFacade.getComponentByNameAndVendorRelease(any(ComponentTypeEnum.class), anyString(), anyString(), any(JsonParseFlagEnum.class))).thenReturn(notFound); + when(toscaOperationFacade.getComponentByNameAndVendorRelease(any(ComponentTypeEnum.class), anyString(), anyString(), + any(JsonParseFlagEnum.class))).thenReturn(notFound); } @Test @@ -301,12 +303,15 @@ public class ResourceImportManagerTest { setResourceBusinessLogicMock(); Either notFound = Either.left(Mockito.mock(Resource.class)); - when(toscaOperationFacade.getComponentByNameAndVendorRelease(any(ComponentTypeEnum.class), anyString(), anyString(), any(JsonParseFlagEnum.class))).thenReturn(notFound); + when(toscaOperationFacade.getComponentByNameAndVendorRelease(any(ComponentTypeEnum.class), anyString(), anyString(), + any(JsonParseFlagEnum.class))).thenReturn(notFound); String jsonContent = ImportUtilsTest.loadFileNameToJsonString("normative-types-new-blockStorage.yml"); ComponentException errorInfoFromTest = null; try { + when(toscaOperationFacade + .getLatestByToscaResourceNameAndModel(resourceMD.getName(), StringUtils.EMPTY)).thenReturn(Either.left(new Resource())); importManager.importNormativeResource(jsonContent, resourceMD, user, true, true); }catch (ComponentException e){ errorInfoFromTest = e; diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogicTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogicTest.java index 17078bcb75..fe2f3ba983 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogicTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/impl/ResourceBusinessLogicTest.java @@ -45,7 +45,6 @@ import java.util.Optional; import java.util.stream.Collectors; import java.util.stream.Stream; import javax.servlet.ServletContext; - import org.apache.commons.lang3.tuple.ImmutablePair; import org.junit.Assert; import org.junit.Before; @@ -90,7 +89,18 @@ import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum; import org.openecomp.sdc.be.facade.operations.CatalogOperation; import org.openecomp.sdc.be.impl.ComponentsUtils; import org.openecomp.sdc.be.impl.WebAppContextWrapper; -import org.openecomp.sdc.be.model.*; +import org.openecomp.sdc.be.model.ArtifactDefinition; +import org.openecomp.sdc.be.model.Component; +import org.openecomp.sdc.be.model.ComponentParametersView; +import org.openecomp.sdc.be.model.DataTypeDefinition; +import org.openecomp.sdc.be.model.GroupDefinition; +import org.openecomp.sdc.be.model.InputDefinition; +import org.openecomp.sdc.be.model.LifeCycleTransitionEnum; +import org.openecomp.sdc.be.model.LifecycleStateEnum; +import org.openecomp.sdc.be.model.NodeTypeInfo; +import org.openecomp.sdc.be.model.PropertyDefinition; +import org.openecomp.sdc.be.model.Resource; +import org.openecomp.sdc.be.model.User; import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ArtifactsOperations; import org.openecomp.sdc.be.model.jsonjanusgraph.operations.InterfaceOperation; @@ -455,6 +465,9 @@ public class ResourceBusinessLogicTest { Resource resource = createResourceObject(false); Resource createdResource = null; try { + when(toscaOperationFacade + .validateComponentNameAndModelExists(resource.getName(), null, ResourceTypeEnum.VFC, ComponentTypeEnum.RESOURCE)) + .thenReturn(Either.left(false)); createdResource = bl.createResource(resource, AuditingActionEnum.CREATE_RESOURCE, user, null, null); assertThat(createResourceObject(true)).isEqualTo(createdResource); } catch (ComponentException e) { @@ -561,6 +574,9 @@ public class ResourceBusinessLogicTest { resourceExist.getTags() .add(resourceName); validateUserRoles(Role.ADMIN, Role.DESIGNER); + when(toscaOperationFacade + .validateComponentNameAndModelExists(resourceName, null, ResourceTypeEnum.VFC, ComponentTypeEnum.RESOURCE)) + .thenReturn(Either.left(true)); try { bl.createResource(resourceExist, AuditingActionEnum.CREATE_RESOURCE, user, null, null); } catch (ComponentException e) { @@ -673,7 +689,9 @@ public class ResourceBusinessLogicTest { private void testResourceIconMissing() { Resource resourceExist = createResourceObject(false); resourceExist.setIcon(null); - + when(toscaOperationFacade + .validateComponentNameAndModelExists(resourceExist.getName(), null, ResourceTypeEnum.VFC, ComponentTypeEnum.RESOURCE)) + .thenReturn(Either.left(false)); try { bl.createResource(resourceExist, AuditingActionEnum.CREATE_RESOURCE, user, null, null); } catch (ComponentException e) { @@ -1541,6 +1559,9 @@ public class ResourceBusinessLogicTest { createRoot(); Resource resourceExist = createResourceObject(false); validateUserRoles(Role.ADMIN, Role.DESIGNER); + when(toscaOperationFacade + .validateComponentNameAndModelExists(resourceExist.getName(), null, ResourceTypeEnum.VFC, ComponentTypeEnum.RESOURCE)) + .thenReturn(Either.left(false)); Resource createdResource = bl.createResource(resourceExist, AuditingActionEnum.CREATE_RESOURCE, user, null, null); createdResource.setLastUpdaterUserId(user.getUserId()); @@ -1549,6 +1570,8 @@ public class ResourceBusinessLogicTest { Either getCompLatestResult = Either.left(createdResource); when(toscaOperationFacade.getLatestByToscaResourceName(resourceExist.getToscaResourceName())) .thenReturn(getCompLatestResult); + when(toscaOperationFacade.getLatestByToscaResourceNameAndModel(resourceExist.getToscaResourceName(), null)) + .thenReturn(getCompLatestResult); when(toscaOperationFacade.overrideComponent(any(Resource.class), any(Resource.class))) .thenReturn(getLatestResult); @@ -1570,6 +1593,9 @@ public class ResourceBusinessLogicTest { public void createOrUpdateResourceCertified() { createRoot(); Resource resourceExist = createResourceObject(false); + when(toscaOperationFacade + .validateComponentNameAndModelExists(resourceExist.getName(), null, ResourceTypeEnum.VFC, ComponentTypeEnum.RESOURCE)) + .thenReturn(Either.left(false)); validateUserRoles(Role.ADMIN, Role.DESIGNER); Resource createdResource = bl.createResource(resourceExist, AuditingActionEnum.CREATE_RESOURCE, user, null, null); @@ -1580,8 +1606,8 @@ public class ResourceBusinessLogicTest { Either getLatestResult = Either.left(createdResource); Either getCompLatestResult = Either.left(createdResource); - when(toscaOperationFacade.getLatestByToscaResourceName(resourceExist.getToscaResourceName())) - .thenReturn(getCompLatestResult); + when(toscaOperationFacade.getLatestByToscaResourceNameAndModel(resourceExist.getToscaResourceName(), null)) + .thenReturn(getCompLatestResult); when(toscaOperationFacade.overrideComponent(any(Resource.class), any(Resource.class))) .thenReturn(getLatestResult); @@ -1608,12 +1634,12 @@ public class ResourceBusinessLogicTest { Resource resourceToUpdtae = createResourceObject(false); Either getLatestResult = Either.right(StorageOperationStatus.NOT_FOUND); - when(toscaOperationFacade.getLatestByName(resourceToUpdtae.getName())).thenReturn(getLatestResult); - - Either getLatestToscaNameResult = Either - .right(StorageOperationStatus.NOT_FOUND); - when(toscaOperationFacade.getLatestByToscaResourceName(resourceToUpdtae.getToscaResourceName())) - .thenReturn(getLatestToscaNameResult); + when(toscaOperationFacade.getLatestByName(resourceToUpdtae.getName(), null)).thenReturn(getLatestResult); + when(toscaOperationFacade.getLatestByToscaResourceNameAndModel(resourceToUpdtae.getToscaResourceName(), null)) + .thenReturn(Either.right(StorageOperationStatus.NOT_FOUND)); + when(toscaOperationFacade + .validateComponentNameAndModelExists(resourceToUpdtae.getName(), null, ResourceTypeEnum.VFC, ComponentTypeEnum.RESOURCE)) + .thenReturn(Either.left(false)); ImmutablePair createOrUpdateResource = bl .createOrUpdateResourceByImport(resourceToUpdtae, user, false, false, false, null, null, false); @@ -1651,13 +1677,19 @@ public class ResourceBusinessLogicTest { String nestedResourceName = bl.buildNestedToscaResourceName(resourceToUpdate.getResourceType() .name(), csarInfo.getVfResourceName(), nodeName) .getRight(); - when(toscaOperationFacade.getLatestByName(resourceToUpdate.getName())) + when(toscaOperationFacade.getLatestByName(resourceToUpdate.getName(), null)) .thenReturn(Either.right(StorageOperationStatus.NOT_FOUND)); when(toscaOperationFacade.getLatestByToscaResourceName(resourceToUpdate.getToscaResourceName())) .thenReturn(Either.right(StorageOperationStatus.NOT_FOUND)); when(toscaOperationFacade.getLatestByToscaResourceName(nestedResourceName)) .thenReturn(Either.right(StorageOperationStatus.NOT_FOUND)); + when(toscaOperationFacade.getLatestByToscaResourceNameAndModel(resourceToUpdate.getToscaResourceName(), null)) + .thenReturn(Either.right(StorageOperationStatus.NOT_FOUND)); + when(toscaOperationFacade + .validateComponentNameAndModelExists(resourceToUpdate.getName(), null, ResourceTypeEnum.VFC, ComponentTypeEnum.RESOURCE)) + .thenReturn(Either.left(false)); + ImmutablePair createOrUpdateResource = bl .createOrUpdateResourceByImport(resourceToUpdate, user, false, false, false, csarInfo, nodeName, false); assertThat(createOrUpdateResource).isNotNull(); @@ -1683,10 +1715,12 @@ public class ResourceBusinessLogicTest { String nestedResourceName = bl.buildNestedToscaResourceName(resourceToUpdate.getResourceType() .name(), csarInfo.getVfResourceName(), nodeName) .getRight(); - when(toscaOperationFacade.getLatestByName(resourceToUpdate.getName())) + when(toscaOperationFacade.getLatestByName(resourceToUpdate.getName(), null)) .thenReturn(Either.right(StorageOperationStatus.NOT_FOUND)); when(toscaOperationFacade.getLatestByToscaResourceName(resourceToUpdate.getToscaResourceName())) .thenReturn(Either.right(StorageOperationStatus.NOT_FOUND)); + when(toscaOperationFacade.getLatestByToscaResourceNameAndModel(resourceToUpdate.getToscaResourceName(), null)) + .thenReturn(Either.right(StorageOperationStatus.NOT_FOUND)); when(toscaOperationFacade.getLatestByToscaResourceName(nestedResourceName)) .thenReturn(Either.left(resourceResponse)); when(toscaOperationFacade.overrideComponent(any(Resource.class), any(Resource.class))) @@ -2067,6 +2101,9 @@ public class ResourceBusinessLogicTest { when(genericTypeBusinessLogic.generateInputsFromGenericTypeProperties(genericVF)).thenCallRealMethod(); when(genericTypeBusinessLogic.convertGenericTypePropertiesToInputsDefintion(genericVF.getProperties(), resource.getUniqueId())).thenCallRealMethod(); + when(toscaOperationFacade + .validateComponentNameAndModelExists(resource.getName(), null, ResourceTypeEnum.VF, ComponentTypeEnum.RESOURCE)) + .thenReturn(Either.left(false)); Resource createdResource = bl.createResource(resource, AuditingActionEnum.CREATE_RESOURCE, user, null, null); assertThat(createdResource).isNotNull(); return createdResource; @@ -2092,6 +2129,9 @@ public class ResourceBusinessLogicTest { when(genericTypeBusinessLogic.generateInputsFromGenericTypeProperties(genericCR)).thenCallRealMethod(); when(genericTypeBusinessLogic.convertGenericTypePropertiesToInputsDefintion(genericCR.getProperties(), resource.getUniqueId())).thenCallRealMethod(); + when(toscaOperationFacade + .validateComponentNameAndModelExists(resource.getName(), null, ResourceTypeEnum.CR, ComponentTypeEnum.RESOURCE)) + .thenReturn(Either.left(false)); Resource createdResource = bl.createResource(resource, AuditingActionEnum.CREATE_RESOURCE, user, null, null); assertThat(createdResource).isNotNull(); return createdResource; @@ -2110,6 +2150,9 @@ public class ResourceBusinessLogicTest { when(genericTypeBusinessLogic.generateInputsFromGenericTypeProperties(genericPNF)).thenCallRealMethod(); when(genericTypeBusinessLogic.convertGenericTypePropertiesToInputsDefintion(genericPNF.getProperties(), resource.getUniqueId())).thenCallRealMethod(); + when(toscaOperationFacade + .validateComponentNameAndModelExists(resource.getName(), null, ResourceTypeEnum.PNF, ComponentTypeEnum.RESOURCE)) + .thenReturn(Either.left(false)); Resource createdResource = bl.createResource(resource, AuditingActionEnum.CREATE_RESOURCE, user, null, null); assertThat(createdResource).isNotNull(); return createdResource; diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/path/beans/ForwardingPathToscaOperationFacade.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/path/beans/ForwardingPathToscaOperationFacade.java index 7102ed2d2b..3774332b23 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/components/path/beans/ForwardingPathToscaOperationFacade.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/path/beans/ForwardingPathToscaOperationFacade.java @@ -21,6 +21,7 @@ package org.openecomp.sdc.be.components.path.beans; import fj.data.Either; +import org.apache.commons.lang3.StringUtils; import org.openecomp.sdc.be.impl.ForwardingPathUtils; import org.openecomp.sdc.be.model.Component; import org.openecomp.sdc.be.model.Resource; @@ -44,13 +45,13 @@ public class ForwardingPathToscaOperationFacade extends ToscaOperationFacade { } @Override - public Either getLatestByName(String resourceName) { + public Either getLatestByName(String resourceName, String model) { if(resourceName.equals(ForwardingPathUtils.FORWARDING_PATH_NODE_NAME) || resourceName.equals(ForwardingPathUtils.FORWARDER_CAPABILITY)){ Resource component = new Resource(); component.setToscaResourceName(GENERIC_SERVICE_NAME); return Either.left((T)component); } - return super.getLatestByName(resourceName); + return super.getLatestByName(resourceName, null); } @Override diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/components/path/utils/GraphTestUtils.java b/catalog-be/src/test/java/org/openecomp/sdc/be/components/path/utils/GraphTestUtils.java index be20beb595..0456988beb 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/components/path/utils/GraphTestUtils.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/components/path/utils/GraphTestUtils.java @@ -73,6 +73,7 @@ public final class GraphTestUtils { vertex.addMetadataProperty(GraphPropertyEnum.UNIQUE_ID, uuid); vertex.addMetadataProperty(GraphPropertyEnum.COMPONENT_TYPE, ComponentTypeEnum.RESOURCE.name()); vertex.addMetadataProperty(GraphPropertyEnum.RESOURCE_TYPE, type.name()); + vertex.addMetadataProperty(GraphPropertyEnum.MODEL, type.name()); vertex.addMetadataProperty(GraphPropertyEnum.IS_ABSTRACT, false); for (Map.Entry prop : metadataProps.entrySet()) { vertex.addMetadataProperty(prop.getKey(), prop.getValue()); diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ArchiveEndpointTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ArchiveEndpointTest.java index 4359270d53..faede3d9c5 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ArchiveEndpointTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ArchiveEndpointTest.java @@ -92,6 +92,7 @@ import org.openecomp.sdc.be.model.jsonjanusgraph.operations.TopologyTemplateOper import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade; import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation; import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus; +import org.openecomp.sdc.be.model.operations.impl.ModelOperation; import org.openecomp.sdc.be.servlets.exception.ComponentExceptionMapper; import org.openecomp.sdc.be.servlets.exception.DefaultExceptionMapper; import org.openecomp.sdc.be.servlets.exception.StorageExceptionMapper; @@ -261,6 +262,11 @@ class ArchiveEndpointTest extends JerseyTest { return new ContainerInstanceTypesData(); } + @Bean + ModelOperation modelOperation() { + return new ModelOperation(null, null, null); + } + private void initGraphForTest() { //Create Catalog Root catalogVertex = GraphTestUtils.createRootCatalogVertex(janusGraphDao); diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ResourceUploadServletTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ResourceUploadServletTest.java new file mode 100644 index 0000000000..31152c63e7 --- /dev/null +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/servlets/ResourceUploadServletTest.java @@ -0,0 +1,289 @@ +/* + * ============LICENSE_START======================================================= + * Copyright (C) 2021 Nordix Foundation + * ================================================================================ + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * ============LICENSE_END========================================================= + */ +package org.openecomp.sdc.be.servlets; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.when; + +import java.io.File; +import java.io.FileReader; +import java.io.IOException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.Optional; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; +import javax.ws.rs.client.Entity; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import org.apache.commons.lang3.tuple.ImmutablePair; +import org.eclipse.jetty.http.HttpStatus; +import org.glassfish.hk2.utilities.binding.AbstractBinder; +import org.glassfish.jersey.client.ClientConfig; +import org.glassfish.jersey.media.multipart.FormDataMultiPart; +import org.glassfish.jersey.media.multipart.MultiPartFeature; +import org.glassfish.jersey.media.multipart.file.FileDataBodyPart; +import org.glassfish.jersey.server.ResourceConfig; +import org.glassfish.jersey.test.JerseyTest; +import org.glassfish.jersey.test.TestProperties; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; +import org.junit.jupiter.api.TestInstance.Lifecycle; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic; +import org.openecomp.sdc.be.components.impl.ModelBusinessLogic; +import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic; +import org.openecomp.sdc.be.components.impl.ResourceImportManager; +import org.openecomp.sdc.be.components.impl.ResponseFormatManager; +import org.openecomp.sdc.be.components.validation.UserValidations; +import org.openecomp.sdc.be.config.ConfigurationManager; +import org.openecomp.sdc.be.config.SpringConfig; +import org.openecomp.sdc.be.dao.api.ActionStatus; +import org.openecomp.sdc.be.impl.ComponentsUtils; +import org.openecomp.sdc.be.impl.ServletUtils; +import org.openecomp.sdc.be.impl.WebAppContextWrapper; +import org.openecomp.sdc.be.model.Model; +import org.openecomp.sdc.be.model.Resource; +import org.openecomp.sdc.be.model.User; +import org.openecomp.sdc.be.servlets.builder.ServletResponseBuilder; +import org.openecomp.sdc.be.servlets.exception.OperationExceptionMapper; +import org.openecomp.sdc.be.user.Role; +import org.openecomp.sdc.be.user.UserBusinessLogic; +import org.openecomp.sdc.common.api.ConfigurationSource; +import org.openecomp.sdc.common.api.Constants; +import org.openecomp.sdc.common.impl.ExternalConfiguration; +import org.openecomp.sdc.common.impl.FSConfigurationSource; +import org.openecomp.sdc.exception.ResponseFormat; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.web.context.WebApplicationContext; + +@TestInstance(Lifecycle.PER_CLASS) +class ResourceUploadServletTest extends JerseyTest { + private static final String USER_ID = "cs0008"; + + @Mock + private HttpServletRequest request; + @Mock + private HttpSession session; + @Mock + private ServletContext servletContext; + @Mock + private WebAppContextWrapper webAppContextWrapper; + @Mock + private WebApplicationContext webApplicationContext; + @Mock + private UserBusinessLogic userBusinessLogic; + @Mock + private ComponentInstanceBusinessLogic componentInstanceBusinessLogic; + @Mock + private ComponentsUtils componentsUtils; + @Mock + private ServletUtils servletUtils; + @Mock + private ResourceImportManager resourceImportManager; + @Mock + private ResourceBusinessLogic resourceBusinessLogic; + @Mock + private ResponseFormat responseFormat; + @Mock + private UserValidations userValidations; + @Mock + private ModelBusinessLogic modelBusinessLogic; + @Mock + private ResponseFormatManager responseFormatManager; + private final String modelName = "ETSI-SOL001-331"; + + private final String rootPath = "/v1/catalog/upload/multipart"; + private Response response; + private User user; + + @BeforeAll + public void initClass() { + when(request.getSession()).thenReturn(session); + when(session.getServletContext()).thenReturn(servletContext); + when(servletContext.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR)) + .thenReturn(webAppContextWrapper); + when(webAppContextWrapper.getWebAppContext(servletContext)).thenReturn(webApplicationContext); + when(webApplicationContext.getBean(ModelBusinessLogic.class)).thenReturn(modelBusinessLogic); + when(request.getHeader(Constants.USER_ID_HEADER)).thenReturn(USER_ID); + when(webApplicationContext.getBean(ServletUtils.class)).thenReturn(servletUtils); + when(servletUtils.getComponentsUtils()).thenReturn(componentsUtils); + final String appConfigDir = "src/test/resources/config/catalog-be"; + final ConfigurationSource configurationSource = new FSConfigurationSource(ExternalConfiguration.getChangeListener(), appConfigDir); + final ConfigurationManager configurationManager = new ConfigurationManager(configurationSource); + final org.openecomp.sdc.be.config.Configuration configuration = new org.openecomp.sdc.be.config.Configuration(); + configuration.setJanusGraphInMemoryGraph(true); + configurationManager.setConfiguration(configuration); + ExternalConfiguration.setAppName("catalog-be"); + } + + @BeforeEach + void resetMock() throws Exception { + super.setUp(); + initTestData(); + } + + @AfterEach + void after() throws Exception { + super.tearDown(); + } + + private void initTestData() { + user = new User(); + user.setUserId(USER_ID); + user.setRole(Role.ADMIN.name()); + when(userBusinessLogic.getUser(USER_ID)).thenReturn(user); + } + + @Override + protected ResourceConfig configure() { + MockitoAnnotations.openMocks(this); + forceSet(TestProperties.CONTAINER_PORT, "0"); + final ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class); + return new ResourceConfig(ResourceUploadServlet.class) + .register(new AbstractBinder() { + @Override + protected void configure() { + bind(request).to(HttpServletRequest.class); + bind(userBusinessLogic).to(UserBusinessLogic.class); + bind(componentInstanceBusinessLogic).to(ComponentInstanceBusinessLogic.class); + bind(componentsUtils).to(ComponentsUtils.class); + bind(servletUtils).to(ServletUtils.class); + bind(resourceImportManager).to(ResourceImportManager.class); + bind(resourceBusinessLogic).to(ResourceBusinessLogic.class); + bind(modelBusinessLogic).to(ModelBusinessLogic.class); + bind(userValidations).to(UserValidations.class); + } + }) + .register(new OperationExceptionMapper(new ServletResponseBuilder(), responseFormatManager)) + .register(MultiPartFeature.class) + .property("contextConfig", context); + } + + @Override + protected void configureClient(final ClientConfig config) { + config.register(MultiPartFeature.class); + } + + @Test + void uploadMultipartWithModelSuccessTest() throws IOException, ParseException, URISyntaxException { + when(responseFormat.getStatus()).thenReturn(HttpStatus.OK_200); + when(componentsUtils.getResponseFormat(ActionStatus.CREATED)).thenReturn(responseFormat); + when(servletUtils.getUserAdmin()).thenReturn(userBusinessLogic); + when(userBusinessLogic.getUser(anyString())).thenReturn(user); + when(resourceBusinessLogic.validatePropertiesDefaultValues(any())).thenReturn(true); + when(resourceImportManager.importNormativeResource(anyString(), any(), any(), anyBoolean(), anyBoolean())) + .thenReturn(new ImmutablePair<>(new Resource(), ActionStatus.CREATED)); + when(modelBusinessLogic.findModel(modelName)).thenReturn(Optional.of(new Model(modelName))); + response = target().path(rootPath).request(MediaType.APPLICATION_JSON) + .header(Constants.USER_ID_HEADER, USER_ID) + .post(Entity.entity(buildFormDataMultiPart("node-types/TestNodeType001.zip", + "src/test/resources/node-types/nodeTypeWithModelsField.json"), MediaType.MULTIPART_FORM_DATA), Response.class); + assertThat(response.getStatus()).isEqualTo(HttpStatus.OK_200); + } + + @Test + void uploadMultipartWithoutModelsFieldSuccessTest() throws IOException, ParseException, URISyntaxException { + when(responseFormat.getStatus()).thenReturn(HttpStatus.OK_200); + when(componentsUtils.getResponseFormat(ActionStatus.CREATED)).thenReturn(responseFormat); + when(servletUtils.getUserAdmin()).thenReturn(userBusinessLogic); + when(userBusinessLogic.getUser(anyString())).thenReturn(user); + when(resourceBusinessLogic.validatePropertiesDefaultValues(any())).thenReturn(true); + when(resourceImportManager.importNormativeResource(anyString(), any(), any(), anyBoolean(), anyBoolean())) + .thenReturn(new ImmutablePair<>(new Resource(), ActionStatus.CREATED)); + response = target().path(rootPath).request(MediaType.APPLICATION_JSON) + .header(Constants.USER_ID_HEADER, USER_ID) + .post(Entity.entity(buildFormDataMultiPart("node-types/TestNodeType002.zip", + "src/test/resources/node-types/nodeTypeWithoutModelsField.json"), MediaType.MULTIPART_FORM_DATA), Response.class); + assertThat(response.getStatus()).isEqualTo(HttpStatus.OK_200); + } + + @Test + void uploadMultipartFailWithEmptyModelsTest() throws IOException, ParseException, URISyntaxException { + when(servletUtils.getUserAdmin()).thenReturn(userBusinessLogic); + when(userBusinessLogic.getUser(anyString())).thenReturn(user); + when(resourceBusinessLogic.validatePropertiesDefaultValues(any())).thenReturn(true); + when(modelBusinessLogic.findModel("")).thenReturn(Optional.empty()); + response = target().path(rootPath).request(MediaType.APPLICATION_JSON) + .header(Constants.USER_ID_HEADER, USER_ID) + .post(Entity.entity(buildFormDataMultiPart("node-types/TestNodeType002.zip", + "src/test/resources/node-types/nodeTypeWithEmptyModels.json"), MediaType.MULTIPART_FORM_DATA), Response.class); + assertThat(response.getStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR_500); + } + + @Test + void uploadMultipartFailWithModelNotFoundTest() throws IOException, ParseException, URISyntaxException { + when(servletUtils.getUserAdmin()).thenReturn(userBusinessLogic); + when(userBusinessLogic.getUser(anyString())).thenReturn(user); + when(resourceBusinessLogic.validatePropertiesDefaultValues(any())).thenReturn(true); + when(modelBusinessLogic.findModel(modelName)).thenReturn(Optional.empty()); + response = target().path(rootPath).request(MediaType.APPLICATION_JSON) + .header(Constants.USER_ID_HEADER, USER_ID) + .post(Entity.entity(buildFormDataMultiPart("node-types/TestNodeType001.zip", + "src/test/resources/node-types/nodeTypeWithModelsField.json"), MediaType.MULTIPART_FORM_DATA), Response.class); + assertThat(response.getStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR_500); + } + + @Test + void uploadMultipartThrowsBusinessExceptionTest() throws IOException, ParseException, URISyntaxException { + when(servletUtils.getUserAdmin()).thenReturn(userBusinessLogic); + when(userBusinessLogic.getUser(anyString())).thenReturn(user); + when(resourceBusinessLogic.validatePropertiesDefaultValues(any())).thenReturn(true); + response = target().path(rootPath).request(MediaType.APPLICATION_JSON) + .header(Constants.USER_ID_HEADER, USER_ID) + .post(Entity.entity(buildFormDataMultiPart("node-types/TestNodeType001.zip", + "src/test/resources/node-types/invalid.json"), MediaType.MULTIPART_FORM_DATA), Response.class); + assertThat(response.getStatus()).isEqualTo(HttpStatus.INTERNAL_SERVER_ERROR_500); + } + + private String getInputData(final String jsonFilename) throws IOException, ParseException { + final JSONObject inputData = (JSONObject) new JSONParser().parse( + new FileReader(jsonFilename)); + return inputData.toJSONString(); + } + + private File getFile(final String fileName) throws URISyntaxException { + final URL resource = this.getClass().getClassLoader().getResource(fileName); + if (resource == null) { + throw new IllegalArgumentException("file not found! " + fileName); + } + return new File(resource.toURI()); + } + + private FormDataMultiPart buildFormDataMultiPart(final String zipFilePath, final String inputJsonData) + throws IOException, ParseException, URISyntaxException { + final FileDataBodyPart filePart = new FileDataBodyPart("resourceZip", getFile(zipFilePath)); + final FormDataMultiPart multipartEntity = new FormDataMultiPart(); + multipartEntity.bodyPart(filePart); + multipartEntity.field("resourceMetadata", getInputData(inputJsonData)); + return multipartEntity; + } +} diff --git a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java index 202cf014fd..acd4cb4128 100644 --- a/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java +++ b/catalog-be/src/test/java/org/openecomp/sdc/be/tosca/ToscaExportHandlerTest.java @@ -735,7 +735,7 @@ public class ToscaExportHandlerTest extends BeConfDependentTest { .thenReturn(Either.left(new ToscaNodeType())); when(toscaOperationFacade.getToscaFullElement("uid")).thenReturn(Either.left(component)); when(toscaOperationFacade.getToscaFullElement("sourceModelUid")).thenReturn(Either.left(component)); - when(toscaOperationFacade.getLatestByName("serviceProxy")).thenReturn(Either.left(new Resource())); + when(toscaOperationFacade.getLatestByName("serviceProxy", null)).thenReturn(Either.left(new Resource())); when(toscaOperationFacade.getToscaElement(any(String.class), any(ComponentParametersView.class))).thenReturn(Either.left(new Resource())); final Map substitutionMappingMap = new HashMap<>(); @@ -859,7 +859,7 @@ public class ToscaExportHandlerTest extends BeConfDependentTest { .thenReturn(Either.left(new ToscaNodeType())); when(toscaOperationFacade.getToscaFullElement("uid")).thenReturn(Either.left(component)); when(toscaOperationFacade.getToscaFullElement("sourceModelUid")).thenReturn(Either.left(component)); - when(toscaOperationFacade.getLatestByName("serviceProxy")).thenReturn(Either.left(new Resource())); + when(toscaOperationFacade.getLatestByName("serviceProxy", null)).thenReturn(Either.left(new Resource())); when(toscaOperationFacade.getToscaElement(any(String.class), any(ComponentParametersView.class))).thenReturn(Either.left(new Resource())); final Map substitutionMappingMap = new HashMap<>(); @@ -1135,7 +1135,7 @@ public class ToscaExportHandlerTest extends BeConfDependentTest { componentInstances.add(instance); container.setComponentInstances(componentInstances); - when(toscaOperationFacade.getLatestByName("serviceProxy")) + when(toscaOperationFacade.getLatestByName("serviceProxy", null)) .thenReturn(Either.right(StorageOperationStatus.BAD_REQUEST)); // test when getLatestByName return is right @@ -1188,7 +1188,7 @@ public class ToscaExportHandlerTest extends BeConfDependentTest { componentInstances.add(instance); container.setComponentInstances(componentInstances); - when(toscaOperationFacade.getLatestByName("serviceProxy")).thenReturn(Either.left(new Resource())); + when(toscaOperationFacade.getLatestByName("serviceProxy", null)).thenReturn(Either.left(new Resource())); ComponentParametersView parameterView = new ComponentParametersView(); parameterView.disableAll(); diff --git a/catalog-be/src/test/resources/node-types/TestNodeType001.yml b/catalog-be/src/test/resources/node-types/TestNodeType001.yml new file mode 100644 index 0000000000..6a312579d8 --- /dev/null +++ b/catalog-be/src/test/resources/node-types/TestNodeType001.yml @@ -0,0 +1,18 @@ +tosca_definitions_version: tosca_simple_yaml_1_0_0 +node_types: + tosca.nodes.nfv.TestNodeType001: + derived_from: tosca.nodes.Root + description: The generic abstract type from which all VNF specific abstract node types shall be derived to form, together with other node types, the TOSCA service template(s) representing the VNFD + properties: + descriptor_id: # instead of vnfd_id + type: string # GUID + description: Globally unique identifier of the VNFD + required: true + descriptor_version: # instead of vnfd_version + type: string + description: Identifies the version of the VNFD + required: true + provider: # instead of vnf_provider + type: string + description: Provider of the VNF and of the VNFD + required: true diff --git a/catalog-be/src/test/resources/node-types/TestNodeType001.zip b/catalog-be/src/test/resources/node-types/TestNodeType001.zip new file mode 100644 index 0000000000..047088ca3f Binary files /dev/null and b/catalog-be/src/test/resources/node-types/TestNodeType001.zip differ diff --git a/catalog-be/src/test/resources/node-types/TestNodeType002.zip b/catalog-be/src/test/resources/node-types/TestNodeType002.zip new file mode 100644 index 0000000000..1185e3ffc1 Binary files /dev/null and b/catalog-be/src/test/resources/node-types/TestNodeType002.zip differ diff --git a/catalog-be/src/test/resources/node-types/invalid.json b/catalog-be/src/test/resources/node-types/invalid.json new file mode 100644 index 0000000000..0db3279e44 --- /dev/null +++ b/catalog-be/src/test/resources/node-types/invalid.json @@ -0,0 +1,3 @@ +{ + +} diff --git a/catalog-be/src/test/resources/node-types/nodeTypeWithEmptyModels.json b/catalog-be/src/test/resources/node-types/nodeTypeWithEmptyModels.json new file mode 100644 index 0000000000..535f7403eb --- /dev/null +++ b/catalog-be/src/test/resources/node-types/nodeTypeWithEmptyModels.json @@ -0,0 +1,16 @@ +{ + "payloadName": "TestNodeType002.yml", + "contactId": "jh0003", + "name": "TestNodeType002", + "description": "TestUploadNodeType002", + "resourceIconPath": "compute", + "resourceType": "VFC", + "categories": [{ + "name": "Generic", + "subcategories": [{ + "name": "Infrastructure" + }] + }], + "tags": ["TestUploadNodeType002"], + "model": "" +} diff --git a/catalog-be/src/test/resources/node-types/nodeTypeWithModelsField.json b/catalog-be/src/test/resources/node-types/nodeTypeWithModelsField.json new file mode 100644 index 0000000000..03e28e3399 --- /dev/null +++ b/catalog-be/src/test/resources/node-types/nodeTypeWithModelsField.json @@ -0,0 +1,16 @@ +{ + "payloadName": "TestNodeType001.yml", + "contactId": "jh0003", + "name": "TestNodeType001", + "description": "TestUploadNodeType001", + "resourceIconPath": "compute", + "resourceType": "VFC", + "categories": [{ + "name": "Generic", + "subcategories": [{ + "name": "Infrastructure" + }] + }], + "tags": ["TestUploadNodeType001"], + "model": "ETSI-SOL001-331" +} diff --git a/catalog-be/src/test/resources/node-types/nodeTypeWithoutModelsField.json b/catalog-be/src/test/resources/node-types/nodeTypeWithoutModelsField.json new file mode 100644 index 0000000000..3c0eb14bb3 --- /dev/null +++ b/catalog-be/src/test/resources/node-types/nodeTypeWithoutModelsField.json @@ -0,0 +1,15 @@ +{ + "payloadName": "TestNodeType002.yml", + "contactId": "jh0003", + "name": "TestNodeType002", + "description": "TestUploadNodeType002", + "resourceIconPath": "compute", + "resourceType": "VFC", + "categories": [{ + "name": "Generic", + "subcategories": [{ + "name": "Infrastructure" + }] + }], + "tags": ["TestUploadNodeType002"] +} -- cgit 1.2.3-korg