From c2a53cb447789e15e85663ae253ab2d90b972934 Mon Sep 17 00:00:00 2001 From: Divyang Patel Date: Tue, 8 Oct 2019 15:19:53 +1100 Subject: Add service creation API as an External API Issue-ID: SDC-2383 Change-Id: I8490163310e892a7ebb6d7e7275edfdeba24045e Signed-off-by: Atif Husain --- .../AuditCreateServiceExternalApiEventFactory.java | 56 + .../externalapi/servlet/CrudExternalServlet.java | 1239 +++++++++++--------- .../org/openecomp/sdc/be/impl/ComponentsUtils.java | 28 + 3 files changed, 738 insertions(+), 585 deletions(-) create mode 100644 catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/externalapi/AuditCreateServiceExternalApiEventFactory.java (limited to 'catalog-be/src/main/java/org') diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/externalapi/AuditCreateServiceExternalApiEventFactory.java b/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/externalapi/AuditCreateServiceExternalApiEventFactory.java new file mode 100644 index 0000000000..640b537f60 --- /dev/null +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/auditing/impl/externalapi/AuditCreateServiceExternalApiEventFactory.java @@ -0,0 +1,56 @@ +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2019 Telstra Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.auditing.impl.externalapi; + +import org.openecomp.sdc.be.model.User; +import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum; +import org.openecomp.sdc.be.resources.data.auditing.model.CommonAuditData; +import org.openecomp.sdc.be.resources.data.auditing.model.DistributionData; +import org.openecomp.sdc.be.resources.data.auditing.model.ResourceCommonInfo; +import org.openecomp.sdc.be.resources.data.auditing.model.ResourceVersionInfo; + +public class AuditCreateServiceExternalApiEventFactory extends AuditExternalApiEventFactory { + + private static final String LOG_STR = "ACTION = \"%s\" RESOURCE_TYPE = \"%s\" CONSUMER_ID = \"%s\"" + + " RESOURCE_URL = \"%s\" MODIFIER = \"%s\" STATUS = \"%s\" SERVICE_INSTANCE_ID = \"%s\" INVARIANT_UUID = \"%s\" DESC = \"%s\""; + + public AuditCreateServiceExternalApiEventFactory(CommonAuditData commonFields, ResourceCommonInfo resourceCommonInfo, + DistributionData distributionData, String invariantUuid, User modifier) { + super(AuditingActionEnum.CREATE_SERVICE_BY_API, commonFields, resourceCommonInfo, distributionData, + ResourceVersionInfo.newBuilder() + .build(), + ResourceVersionInfo.newBuilder() + .build(), + invariantUuid, modifier, null); + } + + @Override + public String getLogPattern() { + return LOG_STR; + } + + @Override + public String[] getLogMessageParams() { + return new String[] {event.getAction(), event.getResourceType(), event.getConsumerId(), + event.getResourceURL(), event.getModifier(), event.getStatus(), + event.getServiceInstanceId(), event.getInvariantUuid(), event.getDesc()}; + } +} diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/CrudExternalServlet.java b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/CrudExternalServlet.java index b4c9c42867..bca588ad14 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/CrudExternalServlet.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/externalapi/servlet/CrudExternalServlet.java @@ -1,585 +1,654 @@ -/*- - * ============LICENSE_START======================================================= - * SDC - * ================================================================================ - * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. - * ================================================================================ - * 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. - * ============LICENSE_END========================================================= - */ - -package org.openecomp.sdc.be.externalapi.servlet; - -import java.io.IOException; -import java.util.Arrays; -import java.util.List; -import java.util.Optional; -import java.util.stream.Collectors; -import javax.inject.Inject; -import javax.inject.Singleton; -import javax.servlet.ServletContext; -import javax.servlet.http.HttpServletRequest; -import javax.ws.rs.Consumes; -import javax.ws.rs.HeaderParam; -import javax.ws.rs.POST; -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.apache.commons.lang3.StringUtils; -import org.elasticsearch.common.Strings; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; -import org.json.simple.parser.ParseException; -import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic; -import org.openecomp.sdc.be.components.impl.ElementBusinessLogic; -import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic; -import org.openecomp.sdc.be.components.impl.ResourceImportManager; -import org.openecomp.sdc.be.components.impl.exceptions.ComponentException; -import org.openecomp.sdc.be.components.lifecycle.LifecycleBusinessLogic; -import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoBase; -import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction; -import org.openecomp.sdc.be.config.BeEcompErrorManager; -import org.openecomp.sdc.be.dao.api.ActionStatus; -import org.openecomp.sdc.be.datamodel.api.CategoryTypeEnum; -import org.openecomp.sdc.be.datatypes.enums.AssetTypeEnum; -import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum; -import org.openecomp.sdc.be.datatypes.enums.FilterKeyEnum; -import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum; -import org.openecomp.sdc.be.ecomp.converters.AssetMetadataConverter; -import org.openecomp.sdc.be.externalapi.servlet.representation.AssetMetadata; -import org.openecomp.sdc.be.impl.ComponentsUtils; -import org.openecomp.sdc.be.impl.ServletUtils; -import org.openecomp.sdc.be.model.Component; -import org.openecomp.sdc.be.model.LifeCycleTransitionEnum; -import org.openecomp.sdc.be.model.Resource; -import org.openecomp.sdc.be.model.User; -import org.openecomp.sdc.be.model.category.CategoryDefinition; -import org.openecomp.sdc.be.model.category.SubCategoryDefinition; -import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum; -import org.openecomp.sdc.be.resources.data.auditing.model.DistributionData; -import org.openecomp.sdc.be.resources.data.auditing.model.ResourceCommonInfo; -import org.openecomp.sdc.be.servlets.AbstractValidationsServlet; -import org.openecomp.sdc.be.servlets.RepresentationUtils; -import org.openecomp.sdc.be.user.UserBusinessLogic; -import org.openecomp.sdc.be.utils.CommonBeUtils; -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.util.ValidationUtils; -import org.openecomp.sdc.exception.ResponseFormat; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.jcabi.aspects.Loggable; -import fj.data.Either; -import io.swagger.v3.oas.annotations.OpenAPIDefinition; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.info.Info; -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.responses.ApiResponses; - -@Loggable(prepend = true, value = Loggable.DEBUG, trim = false) -@Path("/v1/catalog") -@OpenAPIDefinition(info = @Info(title = "CRUD External Servlet", - description = "This Servlet serves external users for creating assets and changing their lifecycle state")) - -@Singleton -public class CrudExternalServlet extends AbstractValidationsServlet { - - @Context - private HttpServletRequest request; - - private static final Logger log = Logger.getLogger(CrudExternalServlet.class); - private final ElementBusinessLogic elementBusinessLogic; - private final AssetMetadataConverter assetMetadataUtils; - private final LifecycleBusinessLogic lifecycleBusinessLogic; - private final ResourceBusinessLogic resourceBusinessLogic; - - @Inject - public CrudExternalServlet(UserBusinessLogic userBusinessLogic, - ComponentInstanceBusinessLogic componentInstanceBL, - ComponentsUtils componentsUtils, ServletUtils servletUtils, - ResourceImportManager resourceImportManager, - ElementBusinessLogic elementBusinessLogic, - AssetMetadataConverter assetMetadataUtils, - LifecycleBusinessLogic lifecycleBusinessLogic, - ResourceBusinessLogic resourceBusinessLogic) { - super(userBusinessLogic, componentInstanceBL, componentsUtils, servletUtils, resourceImportManager); - this.elementBusinessLogic = elementBusinessLogic; - this.assetMetadataUtils = assetMetadataUtils; - this.lifecycleBusinessLogic = lifecycleBusinessLogic; - this.resourceBusinessLogic = resourceBusinessLogic; - } - - /** - * Creates a new Resource - * - * @param assetType - * @param data - * @param userId - * @param instanceIdHeader - * @return - */ - @POST - @Path("/{assetType}") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - @Operation(description = "creates a resource", method = "POST", summary = "Creates a resource") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "ECOMP component is authenticated and Asset created", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = Resource.class)))), - @ApiResponse(responseCode = "400", description = "Missing X-ECOMP-InstanceID HTTP header - POL5001"), - @ApiResponse(responseCode = "401", - description = "ECOMP component should authenticate itself and to re-send again HTTP request with its Basic Authentication credentials - POL5002"), - @ApiResponse(responseCode = "403", description = "ECOMP component is not authorized - POL5003"), - @ApiResponse(responseCode = "404", - description = "Error: Requested '%1' (uuid) resource was not found - SVC4063"), - @ApiResponse(responseCode = "405", - description = "Method Not Allowed : Invalid HTTP method type used ( PUT,DELETE,POST will be rejected) - POL4050"), - @ApiResponse(responseCode = "500", - description = "The GET request failed either due to internal SDC problem. ECOMP Component should continue the attempts to get the needed information - POL5000"), - @ApiResponse(responseCode = "400", - description = "The name provided for the newly created resource is already in use for another resource in SDC - SVC4050"), - @ApiResponse(responseCode = "400", - description = "Invalid field format. One of the provided fields does not comply with the field rules - SVC4126"), - @ApiResponse(responseCode = "400", - description = "Missing request body. The post request did not contain the expected body - SVC4500"), - @ApiResponse(responseCode = "400", - description = "The resource name is missing in the request body - SVC4062"), - @ApiResponse(responseCode = "400", - description = "Create VFCMT request: VFCMT description has wrong format - SVC4064"), - @ApiResponse(responseCode = "400", - description = "Create VFCMT request: VFCMT description has wrong format (exceeds limit) - SVC4065"), - @ApiResponse(responseCode = "400", - description = "Create VFCMT request: VFCMT tags exceeds character limit - SVC4066"), - @ApiResponse(responseCode = "400", - description = "Create VFCMT request: VFCMT vendor name exceeds character limit - SVC4067"), - @ApiResponse(responseCode = "400", - description = "Create VFCMT request: VFCMT vendor release exceeds character limit - SVC4068"), - @ApiResponse(responseCode = "400", - description = "Create VFCMT request: VFCMT ATT Contact has wrong format - SVC4069"), - @ApiResponse(responseCode = "400", - description = "Create VFCMT request: VFCMT name has wrong format - SVC4070"), - @ApiResponse(responseCode = "400", - description = "Create VFCMT request: VFCMT vendor name has wrong format - SVC4071"), - @ApiResponse(responseCode = "400", - description = "Create VFCMT request: VFCMT vendor release has wrong format - SVC4072"), - @ApiResponse(responseCode = "400", - description = "Create VFCMT request: VFCMT name exceeds character limit - SVC4073")}) - // @ApiImplicitParams({@ApiImplicitParam(required = true, dataType = - // "org.openecomp.sdc.be.model.Resource", paramType = "body", value = "json describe the created - // resource")}) - public Response createResourceExternal( - @Parameter(description = "Determines the format of the body of the request", - required = true) @HeaderParam(value = Constants.CONTENT_TYPE_HEADER) String contentType, - @Parameter(description = "The user id", - required = true) @HeaderParam(value = Constants.USER_ID_HEADER) final String userId, - @Parameter(description = "X-ECOMP-RequestID header", - required = false) @HeaderParam(value = Constants.X_ECOMP_REQUEST_ID_HEADER) String requestId, - @Parameter(description = "X-ECOMP-InstanceID header", required = true) @HeaderParam( - value = Constants.X_ECOMP_INSTANCE_ID_HEADER) final String instanceIdHeader, - @Parameter(description = "Determines the format of the body of the response", - required = false) @HeaderParam(value = Constants.ACCEPT_HEADER) String accept, - @Parameter(description = "The username and password", - required = true) @HeaderParam(value = Constants.AUTHORIZATION_HEADER) String authorization, - @Parameter(description = "The requested asset type", required = true, - schema = @Schema( - allowableValues = {"resources, services"})) @PathParam("assetType") final String assetType, - @Parameter(hidden = true) String data) { - - init(); - - Wrapper responseWrapper = new Wrapper<>(); - String requestURI = request.getRequestURI(); - String url = request.getMethod() + " " + requestURI; - log.debug("Start handle request of {}", url); - Resource resource = null; - User modifier = null; - ResourceCommonInfo resourceCommonInfo = new ResourceCommonInfo(ComponentTypeEnum.RESOURCE.getValue()); - - ServletContext context = request.getSession().getServletContext(); - try { - // Validate X-ECOMP-InstanceID Header - if (responseWrapper.isEmpty()) { - validateXECOMPInstanceIDHeader(instanceIdHeader, responseWrapper); - } - // Validate USER_ID Header - if (responseWrapper.isEmpty()) { - validateHttpCspUserIdHeader(userId, responseWrapper); - } - // Validate assetType - if (responseWrapper.isEmpty()) { - if( !AssetTypeEnum.RESOURCES.getValue().equals(assetType) ){ - responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.RESTRICTED_OPERATION)); - } - } - //Validate resource type - if(responseWrapper.isEmpty()){ - JSONParser parser = new JSONParser(); - JSONObject jsonObj = (JSONObject) parser.parse(data); - String resourceType = (String) jsonObj.get(FilterKeyEnum.RESOURCE_TYPE.getName()); - if( StringUtils.isEmpty(resourceType) || !ResourceTypeEnum.containsName(resourceType) ){ - resourceCommonInfo.setResourceName((String) jsonObj.get("name")); - responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT)); - } - } - // Convert the user json to a resource - if (responseWrapper.isEmpty()) { - modifier = new User(); - modifier.setUserId(userId); - Either eitherResource = getComponentsUtils() - .convertJsonToObjectUsingObjectMapper(data, modifier, Resource.class, - null, ComponentTypeEnum.RESOURCE); - if( eitherResource.isRight() ){ - responseWrapper.setInnerElement(eitherResource.right().value()); - } - else{ - resource = eitherResource.left().value(); - } - - } - //validate name exist - if(responseWrapper.isEmpty()){ - if( Strings.isEmpty(resource.getName())){ - responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat( - ActionStatus.MISSING_COMPONENT_NAME, ComponentTypeEnum.RESOURCE.getValue())); - - } - } - - if(responseWrapper.isEmpty()){ - resource.setDerivedFrom(Arrays.asList("tosca.nodes.Root")); - resource.setSystemName(ValidationUtils.convertToSystemName(resource.getName())); - resource.setToscaResourceName(CommonBeUtils.generateToscaResourceName(ResourceTypeEnum.VFCMT.name(), - resource.getSystemName())); - handleCategories(context, data, resource, responseWrapper); - } - // Create the resource in the dataModel - if (responseWrapper.isEmpty()) { - resource = resourceBusinessLogic.createResource(resource, null, - modifier, null, null); - return buildCreatedResourceResponse(resource, context, responseWrapper); - } else { - return buildErrorResponse(responseWrapper.getInnerElement()); - } - } catch (IOException|ParseException e) { - final String message = "failed to create vfc monitoring template resource"; - BeEcompErrorManager.getInstance().logBeRestApiGeneralError(message); - log.debug(message, e); - responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)); - return buildErrorResponse(responseWrapper.getInnerElement()); - } catch (ComponentException e){ - ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(e); - responseWrapper.setInnerElement(responseFormat); - return buildErrorResponse(responseFormat); - } - finally{ - getComponentsUtils().auditCreateResourceExternalApi(responseWrapper.getInnerElement(), resourceCommonInfo, request, resource); - } - } - - /** - * Changing the lifecycle of an asset - * @param jsonChangeInfo The description - request body - * @param assetType The requested asset type.Valid values are: resources / services (for VFCMT – use "resources") - * @param uuid The uuid of the desired resource to be changed - * @param lifecycleTransition The lifecycle operation to be performed on the asset.Valid values are:Checkin / Checkout / CERTIFICATION_REQUEST - * @param userId - * @return - */ - @POST - @Path("/{assetType}/{uuid}/lifecycleState/{lifecycleOperation}") - @Consumes(MediaType.APPLICATION_JSON) - @Produces(MediaType.APPLICATION_JSON) - @Operation(description = "Change Resource lifecycle State", method = "POST") - @ApiResponses(value = { - @ApiResponse(responseCode = "200", description = "Resource state changed", - content = @Content(array = @ArraySchema(schema = @Schema(implementation = AssetMetadata.class)))), - @ApiResponse(responseCode = "400", description = "Missing X-ECOMP-InstanceID HTTP header - POL5001"), - @ApiResponse(responseCode = "401", - description = "ECOMP component should authenticate itself and to re-send again HTTP request with its Basic Authentication credentials - POL5002"), - @ApiResponse(responseCode = "403", description = "ECOMP component is not authorized - POL5003"), - @ApiResponse(responseCode = "404", - description = "Error: Requested '%1' (uuid) resource was not found - SVC4063"), - @ApiResponse(responseCode = "405", - description = "Method Not Allowed : Invalid HTTP method type used ( PUT,DELETE,POST will be rejected) - POL4050"), - @ApiResponse(responseCode = "500", - description = "The GET request failed either due to internal SDC problem. ECOMP Component should continue the attempts to get the needed information - POL5000"), - @ApiResponse(responseCode = "403", description = "Asset is already checked-out by another user - SVC4085"), - @ApiResponse(responseCode = "403", - description = "Asset is being edited by different user. Only one user can checkout and edit an asset on given time. The asset will be available for checkout after the other user will checkin the asset - SVC4080")}) - // @ApiImplicitParams({@ApiImplicitParam(required = true, dataType = "org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction", paramType = "body", value = "userRemarks - Short description (free text) about the asset version being changed")}) - public Response changeResourceStateExternal( - @Parameter(description = "Determines the format of the body of the request", - required = true) @HeaderParam(value = Constants.CONTENT_TYPE_HEADER) String contentType, - @Parameter(description = "The user id", - required = true) @HeaderParam(value = Constants.USER_ID_HEADER) final String userId, - @Parameter(description = "X-ECOMP-RequestID header", - required = false) @HeaderParam(value = Constants.X_ECOMP_REQUEST_ID_HEADER) String requestId, - @Parameter(description = "X-ECOMP-InstanceID header", required = true) @HeaderParam( - value = Constants.X_ECOMP_INSTANCE_ID_HEADER) final String instanceIdHeader, - @Parameter(description = "Determines the format of the body of the response", - required = false) @HeaderParam(value = Constants.ACCEPT_HEADER) String accept, - @Parameter(description = "The username and password", - required = true) @HeaderParam(value = Constants.AUTHORIZATION_HEADER) String authorization, - @Parameter(schema = @Schema(allowableValues = {"checkout, checkin"}), - required = true) @PathParam(value = "lifecycleOperation") final String lifecycleTransition, - @Parameter(description = "id of component to be changed") @PathParam(value = "uuid") final String uuid, - @Parameter(description = "validValues: resources / services ", - schema = @Schema(allowableValues = {ComponentTypeEnum.RESOURCE_PARAM_NAME , - ComponentTypeEnum.SERVICE_PARAM_NAME})) @PathParam( - value = "assetType") final String assetType, - @Parameter(hidden = true) String jsonChangeInfo) { - - Response response = null; - - init(); - - String requestURI = request.getRequestURI(); - String url = request.getMethod() + " " + requestURI; - log.debug("Start handle request of {}", url); - - //get the business logic - ServletContext context = request.getSession().getServletContext(); - - Wrapper responseWrapper = runValidations(assetType); - ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType); - Component component = null; - Component responseObject = null; - User modifier = null; - - try{ - // Validate X-ECOMP-InstanceID Header - if (responseWrapper.isEmpty()) { - validateXECOMPInstanceIDHeader(instanceIdHeader, responseWrapper); - } - - if (responseWrapper.isEmpty()) { - //get user - Either eitherGetUser = getUser(request, userId); - if (eitherGetUser.isRight()) { - ResponseFormat responseFormat = eitherGetUser.right().value(); - responseWrapper.setInnerElement(responseFormat); - return buildErrorResponse(responseFormat); - } - modifier = eitherGetUser.left().value(); - - //get the component id from the uuid - Either latestVersion = lifecycleBusinessLogic.getLatestComponentByUuid(componentType, uuid); - if (latestVersion.isRight()) { - ResponseFormat responseFormat = latestVersion.right().value(); - responseWrapper.setInnerElement(responseFormat); - return buildErrorResponse(responseFormat); - } - component = latestVersion.left().value(); - String componentId = component.getUniqueId(); - - //validate the transition is valid - Either validateEnum = validateTransitionEnum(lifecycleTransition, modifier); - if (validateEnum.isRight()) { - ResponseFormat responseFormat = validateEnum.right().value(); - responseWrapper.setInnerElement(responseFormat); - return buildErrorResponse(responseFormat); - } - LifeCycleTransitionEnum transitionEnum = validateEnum.left().value(); - - //create changeInfo - LifecycleChangeInfoWithAction changeInfo = new LifecycleChangeInfoWithAction(); - try { - if (jsonChangeInfo != null && !jsonChangeInfo.isEmpty()) { - ObjectMapper mapper = new ObjectMapper(); - changeInfo = new LifecycleChangeInfoWithAction(mapper.readValue(jsonChangeInfo, LifecycleChangeInfoBase.class).getUserRemarks()); - } - } - catch (IOException e) { - BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject"); - log.debug("failed to convert from json {}", jsonChangeInfo, e); - ResponseFormat responseFormat = getComponentsUtils().getInvalidContentErrorAndAudit(modifier, componentId, AuditingActionEnum.CHECKOUT_RESOURCE); - responseWrapper.setInnerElement(responseFormat); - return buildErrorResponse(responseFormat); - } - - //execute business logic - Either actionResponse = lifecycleBusinessLogic - .changeComponentState(componentType, componentId, modifier, transitionEnum, changeInfo, false, true); - if (actionResponse.isRight()) { - log.info("failed to change resource state"); - ResponseFormat responseFormat = actionResponse.right().value(); - responseWrapper.setInnerElement(responseFormat); - return buildErrorResponse(responseFormat); - } - - log.debug("change state successful !!!"); - responseObject = actionResponse.left().value(); - response = buildCreatedResourceResponse(responseObject, context, responseWrapper); - } else { - response = buildErrorResponse(responseWrapper.getInnerElement()); - } - - return response; - } catch (IOException e) { - BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Change Lifecycle State"); - log.debug("change lifecycle state failed with exception", e); - ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR); - responseWrapper.setInnerElement(responseFormat); - return buildErrorResponse(responseFormat); - } catch (ComponentException e){ - ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(e); - responseWrapper.setInnerElement(responseFormat); - return buildErrorResponse(responseFormat); - } - finally{ - getComponentsUtils().auditChangeLifecycleAction(responseWrapper.getInnerElement(), componentType, requestId, - component, responseObject, new DistributionData(instanceIdHeader, requestURI), modifier); - } - } - - private Response buildCreatedResourceResponse(Component resource, ServletContext context, - Wrapper responseWrapper) throws IOException { - ResponseFormat responseFormat; - Response response; - Either resMetadata = assetMetadataUtils - .convertToSingleAssetMetadata(resource, request.getRequestURL().toString(), - true); - if (resMetadata.isRight()) { - log.debug("Asset conversion Failed"); - responseFormat = resMetadata.right().value(); - responseWrapper.setInnerElement(responseFormat); - response = buildErrorResponse(responseFormat); - }else{ - final AssetMetadata assetData = resMetadata.left().value(); - assetData.setToscaModelURL(null); - - responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.CREATED)); - Object representation = RepresentationUtils.toRepresentation(assetData); - response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), representation); - } - return response; - } - - private void handleCategories(ServletContext context, String data, Resource resource, - Wrapper responseWrapper) { - try { - JSONParser parser = new JSONParser(); - JSONObject jsonObj = (JSONObject) parser.parse(data); - String category = (String) jsonObj.get(CategoryTypeEnum.CATEGORY.getValue()); - String subcategory = (String) jsonObj.get(CategoryTypeEnum.SUBCATEGORY.getValue()); - if (Strings.isEmpty(category)) { - responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat( - ActionStatus.COMPONENT_MISSING_CATEGORY, ComponentTypeEnum.RESOURCE.getValue())); - } - else if (Strings.isEmpty(subcategory)) { - responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat( - ActionStatus.COMPONENT_MISSING_SUBCATEGORY)); - } - if (responseWrapper.isEmpty()) { - // get All Categories - Either, ActionStatus> allResourceCategories = elementBusinessLogic - .getAllResourceCategories(); - // Error fetching categories - if (allResourceCategories.isRight()) { - responseWrapper.setInnerElement( - getComponentsUtils().getResponseFormat(allResourceCategories.right().value())); - } else { - addCategories(resource, category, subcategory, allResourceCategories, responseWrapper); - } - } - } catch (ParseException e) { - log.debug("Exception occured in addCategories: {}", e.getMessage(), e); - responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)); - } - - } - - private void addCategories(Resource resource, String category, String subcategory, - Either, ActionStatus> allResourceCategories, - Wrapper responseWrapper) { - Optional optionalCategory = - // Stream of all the categories - allResourceCategories.left().value().stream() - // filter in only relevant category - .filter(e -> e.getName().equals(category)) - // get the result - .findAny(); - if (!optionalCategory.isPresent()) { - responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat( - ActionStatus.COMPONENT_INVALID_CATEGORY, ComponentTypeEnum.RESOURCE.getValue())); - } else { - CategoryDefinition categoryDefinition = optionalCategory.get(); - - List subCaregories = - // Stream of all sub-categories of the relevant - // category - categoryDefinition.getSubcategories().stream() - // filter in only relevant sub-category - .filter(e -> e.getName().equals(subcategory)) - // get the result - .collect(Collectors.toList()); - - if( subCaregories.isEmpty() ){ - responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat( - ActionStatus.COMPONENT_INVALID_SUBCATEGORY, ComponentTypeEnum.RESOURCE.getValue())); - } - else{ - categoryDefinition.setSubcategories(subCaregories); - resource.setCategories(Arrays.asList(categoryDefinition)); - } - - } - } - - - - - - - private Wrapper runValidations(final String assetType) { - Wrapper responseWrapper = new Wrapper<>(); - - // Validate X-ECOMP-InstanceID Header - if (responseWrapper.isEmpty()) { - String instanceId = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER); - validateXECOMPInstanceIDHeader(instanceId,responseWrapper); - } - // Validate USER_ID Header - if (responseWrapper.isEmpty()) { - validateHttpCspUserIdHeader(request.getHeader(Constants.USER_ID_HEADER),responseWrapper); - } - // Validate assetType - if (responseWrapper.isEmpty()) { - if( !AssetTypeEnum.RESOURCES.getValue().equals(assetType) && !AssetTypeEnum.SERVICES.getValue().equals(assetType)){ - responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.RESTRICTED_OPERATION)); - } - } - - return responseWrapper; - } - - private Either validateTransitionEnum(final String lifecycleTransition, User user) { - LifeCycleTransitionEnum transitionEnum = LifeCycleTransitionEnum.CHECKOUT; - try { - transitionEnum = LifeCycleTransitionEnum.getFromDisplayName(lifecycleTransition); - } catch (IllegalArgumentException e) { - log.info("state operation is not valid. operations allowed are: {}", LifeCycleTransitionEnum.valuesAsString(), e); - ResponseFormat error = getComponentsUtils().getInvalidContentErrorAndAudit(user, "", AuditingActionEnum.CHECKOUT_RESOURCE); - return Either.right(error); - } - return Either.left(transitionEnum); - } - -} +/*- + * ============LICENSE_START======================================================= + * SDC + * ================================================================================ + * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved. + * ================================================================================ + * 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. + * ============LICENSE_END========================================================= + */ + +package org.openecomp.sdc.be.externalapi.servlet; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.jcabi.aspects.Loggable; +import fj.data.Either; +import io.swagger.v3.oas.annotations.OpenAPIDefinition; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.info.Info; +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.responses.ApiResponses; +import org.apache.commons.lang3.StringUtils; +import org.elasticsearch.common.Strings; +import org.json.simple.JSONObject; +import org.json.simple.parser.JSONParser; +import org.json.simple.parser.ParseException; +import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic; +import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic; +import org.openecomp.sdc.be.components.impl.ElementBusinessLogic; +import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic; +import org.openecomp.sdc.be.components.impl.ResourceImportManager; +import org.openecomp.sdc.be.components.impl.exceptions.ComponentException; +import org.openecomp.sdc.be.components.lifecycle.LifecycleBusinessLogic; +import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoBase; +import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction; +import org.openecomp.sdc.be.config.BeEcompErrorManager; +import org.openecomp.sdc.be.dao.api.ActionStatus; +import org.openecomp.sdc.be.datamodel.api.CategoryTypeEnum; +import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum; +import org.openecomp.sdc.be.datatypes.enums.AssetTypeEnum; +import org.openecomp.sdc.be.datatypes.enums.FilterKeyEnum; +import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum; +import org.openecomp.sdc.be.datatypes.enums.ExternalCategoryTypeEnum; +import org.openecomp.sdc.be.ecomp.converters.AssetMetadataConverter; +import org.openecomp.sdc.be.externalapi.servlet.representation.AssetMetadata; +import org.openecomp.sdc.be.impl.ComponentsUtils; +import org.openecomp.sdc.be.impl.ServletUtils; +import org.openecomp.sdc.be.model.Resource; +import org.openecomp.sdc.be.model.Service; +import org.openecomp.sdc.be.model.User; +import org.openecomp.sdc.be.model.Component; +import org.openecomp.sdc.be.model.LifeCycleTransitionEnum; +import org.openecomp.sdc.be.model.category.CategoryDefinition; +import org.openecomp.sdc.be.model.category.SubCategoryDefinition; +import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum; +import org.openecomp.sdc.be.resources.data.auditing.model.DistributionData; +import org.openecomp.sdc.be.resources.data.auditing.model.ResourceCommonInfo; +import org.openecomp.sdc.be.servlets.AbstractValidationsServlet; +import org.openecomp.sdc.be.servlets.RepresentationUtils; +import org.openecomp.sdc.be.user.UserBusinessLogic; +import org.openecomp.sdc.be.utils.CommonBeUtils; +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.util.ValidationUtils; +import org.openecomp.sdc.exception.ResponseFormat; + +import javax.inject.Inject; +import javax.inject.Singleton; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpServletRequest; +import javax.ws.rs.Path; +import javax.ws.rs.Consumes; +import javax.ws.rs.Produces; +import javax.ws.rs.POST; +import javax.ws.rs.HeaderParam; +import javax.ws.rs.PathParam; +import javax.ws.rs.core.Context; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Loggable(prepend = true, value = Loggable.DEBUG, trim = false) +@Path("/v1/catalog") +@OpenAPIDefinition(info = @Info(title = "CRUD External Servlet", + description = "This Servlet serves external users for creating assets and changing their lifecycle state")) + +@Singleton +public class CrudExternalServlet extends AbstractValidationsServlet { + + @Context + private HttpServletRequest request; + + private static final Logger log = Logger.getLogger(CrudExternalServlet.class); + private final ElementBusinessLogic elementBusinessLogic; + private final AssetMetadataConverter assetMetadataUtils; + private final LifecycleBusinessLogic lifecycleBusinessLogic; + private final ResourceBusinessLogic resourceBusinessLogic; + private final ServiceBusinessLogic serviceBusinessLogic; + + @Inject + public CrudExternalServlet(UserBusinessLogic userBusinessLogic, + ComponentInstanceBusinessLogic componentInstanceBL, + ComponentsUtils componentsUtils, ServletUtils servletUtils, + ResourceImportManager resourceImportManager, + ElementBusinessLogic elementBusinessLogic, + AssetMetadataConverter assetMetadataUtils, + LifecycleBusinessLogic lifecycleBusinessLogic, + ResourceBusinessLogic resourceBusinessLogic, + ServiceBusinessLogic serviceBusinessLogic) { + super(userBusinessLogic, componentInstanceBL, componentsUtils, servletUtils, resourceImportManager); + this.elementBusinessLogic = elementBusinessLogic; + this.assetMetadataUtils = assetMetadataUtils; + this.lifecycleBusinessLogic = lifecycleBusinessLogic; + this.resourceBusinessLogic = resourceBusinessLogic; + this.serviceBusinessLogic = serviceBusinessLogic; + } + + /** + * Creates a new Asset (Resource or Service) + * + * @param assetType + * @param data + * @param userId + * @param instanceIdHeader + * @return + */ + @POST + @Path("/{assetType}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(description = "creates an asset (resource or service)", method = "POST", summary = "Creates an asset (resource or service)") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "ECOMP component is authenticated and Asset created", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = Resource.class)))), + @ApiResponse(responseCode = "400", description = "Missing X-ECOMP-InstanceID HTTP header - POL5001"), + @ApiResponse(responseCode = "401", + description = "ECOMP component should authenticate itself and to re-send again HTTP request with its Basic Authentication credentials - POL5002"), + @ApiResponse(responseCode = "403", description = "ECOMP component is not authorized - POL5003"), + @ApiResponse(responseCode = "404", + description = "Error: Requested '%1' (uuid) resource was not found - SVC4063"), + @ApiResponse(responseCode = "405", + description = "Method Not Allowed : Invalid HTTP method type used ( PUT,DELETE,POST will be rejected) - POL4050"), + @ApiResponse(responseCode = "500", + description = "The GET request failed either due to internal SDC problem. ECOMP Component should continue the attempts to get the needed information - POL5000"), + @ApiResponse(responseCode = "400", + description = "The name provided for the newly created resource is already in use for another resource in SDC - SVC4050"), + @ApiResponse(responseCode = "400", + description = "Invalid field format. One of the provided fields does not comply with the field rules - SVC4126"), + @ApiResponse(responseCode = "400", + description = "Missing request body. The post request did not contain the expected body - SVC4500"), + @ApiResponse(responseCode = "400", + description = "The resource name is missing in the request body - SVC4062"), + @ApiResponse(responseCode = "400", + description = "Create VFCMT request: VFCMT description has wrong format - SVC4064"), + @ApiResponse(responseCode = "400", + description = "Create VFCMT request: VFCMT description has wrong format (exceeds limit) - SVC4065"), + @ApiResponse(responseCode = "400", + description = "Create VFCMT request: VFCMT tags exceeds character limit - SVC4066"), + @ApiResponse(responseCode = "400", + description = "Create VFCMT request: VFCMT vendor name exceeds character limit - SVC4067"), + @ApiResponse(responseCode = "400", + description = "Create VFCMT request: VFCMT vendor release exceeds character limit - SVC4068"), + @ApiResponse(responseCode = "400", + description = "Create VFCMT request: VFCMT ATT Contact has wrong format - SVC4069"), + @ApiResponse(responseCode = "400", + description = "Create VFCMT request: VFCMT name has wrong format - SVC4070"), + @ApiResponse(responseCode = "400", + description = "Create VFCMT request: VFCMT vendor name has wrong format - SVC4071"), + @ApiResponse(responseCode = "400", + description = "Create VFCMT request: VFCMT vendor release has wrong format - SVC4072"), + @ApiResponse(responseCode = "400", + description = "Create VFCMT request: VFCMT name exceeds character limit - SVC4073"), + @ApiResponse(responseCode = "400", description = "Invalid Content. Missing PROJECT_CODE number - SVC4129"), + @ApiResponse(responseCode = "409", description = "Error: %1 (Service) with name '%2' already exists. - SVC4050")}) + // @ApiImplicitParams({@ApiImplicitParam(required = true, dataType = + // "org.openecomp.sdc.be.model.Resource", paramType = "body", value = "json describe the created + // resource")}) + public Response createComponentExternal( + @Parameter(description = "Determines the format of the body of the request", + required = true) @HeaderParam(value = Constants.CONTENT_TYPE_HEADER) String contentType, + @Parameter(description = "The user id", + required = true) @HeaderParam(value = Constants.USER_ID_HEADER) final String userId, + @Parameter(description = "X-ECOMP-RequestID header", + required = false) @HeaderParam(value = Constants.X_ECOMP_REQUEST_ID_HEADER) String requestId, + @Parameter(description = "X-ECOMP-InstanceID header", required = true) @HeaderParam( + value = Constants.X_ECOMP_INSTANCE_ID_HEADER) final String instanceIdHeader, + @Parameter(description = "Determines the format of the body of the response", + required = false) @HeaderParam(value = Constants.ACCEPT_HEADER) String accept, + @Parameter(description = "The username and password", + required = true) @HeaderParam(value = Constants.AUTHORIZATION_HEADER) String authorization, + @Parameter(description = "The requested asset type", required = true, + schema = @Schema( + allowableValues = {"resources, services"})) @PathParam("assetType") final String assetType, + @Parameter(hidden = true) String data) { + + init(); + + Wrapper responseWrapper = new Wrapper<>(); + String requestURI = request.getRequestURI(); + String url = request.getMethod() + " " + requestURI; + log.debug("Start handle request of {}", url); + Resource resource = null; + User modifier = null; + ResourceCommonInfo resourceCommonInfo = new ResourceCommonInfo(ComponentTypeEnum.RESOURCE.getValue()); + Service service = null; + + ServletContext context = request.getSession().getServletContext(); + try { + // Validate X-ECOMP-InstanceID Header + if (responseWrapper.isEmpty()) { + validateXECOMPInstanceIDHeader(instanceIdHeader, responseWrapper); + } + // Validate USER_ID Header + if (responseWrapper.isEmpty()) { + validateHttpCspUserIdHeader(userId, responseWrapper); + } + // Validate assetType + + if( responseWrapper.isEmpty() && !(AssetTypeEnum.RESOURCES.getValue().equals(assetType) || AssetTypeEnum.SERVICES.getValue().equals(assetType))) { + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.RESTRICTED_OPERATION)); + } + + if (responseWrapper.isEmpty() && AssetTypeEnum.SERVICES.getValue().equals(assetType)) { + + modifier = new User(); + modifier.setUserId(userId); + Either convertResponse = getComponentsUtils() + .convertJsonToObjectUsingObjectMapper(data, modifier, Service.class, + null, ComponentTypeEnum.SERVICE); + if( convertResponse.isRight() ){ + responseWrapper.setInnerElement(convertResponse.right().value()); + } + else{ + service = convertResponse.left().value(); + } + + if (service==null){ + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat( + ActionStatus.SERVICE_NOT_FOUND, ComponentTypeEnum.SERVICE.getValue())); + } + + //validate name exist + if(responseWrapper.isEmpty() && Strings.isEmpty(service.getName())){ + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat( + ActionStatus.MISSING_COMPONENT_NAME, ComponentTypeEnum.SERVICE.getValue())); + } + + //validate category + if(responseWrapper.isEmpty() && service.getCategories().size()>0 && !ExternalCategoryTypeEnum.containsIgnoreCase(service.getCategories().get(0).getName())){ + log.debug("Service category is not supported {}", service.getCategories().get(0).getName()); + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat( + ActionStatus.COMPONENT_INVALID_CATEGORY, ComponentTypeEnum.SERVICE.getValue())); + } + + if(responseWrapper.isEmpty()){ + service.setSystemName(ValidationUtils.convertToSystemName(service.getName())); + log.debug("Service system name :"+service.getSystemName()); + } + + if(responseWrapper.isEmpty()){ + Either actionResponse = serviceBusinessLogic.createService(service, modifier); + if (actionResponse.isRight()) { + log.debug("Failed to create service"); + responseWrapper.setInnerElement(actionResponse.right().value()); + return buildErrorResponse(responseWrapper.getInnerElement()); + } + + // Create the service in the dataModel + service = actionResponse.left().value(); + Object result = RepresentationUtils.toRepresentation(actionResponse.left().value()); + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.CREATED)); + return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), result); + } + else{ + return buildErrorResponse(responseWrapper.getInnerElement()); + } + + + + } else { + //Validate resource type + if(responseWrapper.isEmpty()){ + JSONParser parser = new JSONParser(); + JSONObject jsonObj = (JSONObject) parser.parse(data); + String resourceType = (String) jsonObj.get(FilterKeyEnum.RESOURCE_TYPE.getName()); + if( StringUtils.isEmpty(resourceType) || !ResourceTypeEnum.containsName(resourceType) ){ + resourceCommonInfo.setResourceName((String) jsonObj.get("name")); + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT)); + } + } + // Convert the user json to a resource + if (responseWrapper.isEmpty()) { + modifier = new User(); + modifier.setUserId(userId); + Either eitherResource = getComponentsUtils() + .convertJsonToObjectUsingObjectMapper(data, modifier, Resource.class, + null, ComponentTypeEnum.RESOURCE); + if( eitherResource.isRight() ){ + responseWrapper.setInnerElement(eitherResource.right().value()); + } + else{ + resource = eitherResource.left().value(); + } + + } + //validate name exist + if(responseWrapper.isEmpty()){ + if( Strings.isEmpty(resource.getName())){ + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat( + ActionStatus.MISSING_COMPONENT_NAME, ComponentTypeEnum.RESOURCE.getValue())); + + } + } + + if(responseWrapper.isEmpty()){ + resource.setDerivedFrom(Arrays.asList("tosca.nodes.Root")); + resource.setSystemName(ValidationUtils.convertToSystemName(resource.getName())); + resource.setToscaResourceName(CommonBeUtils.generateToscaResourceName(ResourceTypeEnum.VFCMT.name(), + resource.getSystemName())); + handleCategories(context, data, resource, responseWrapper); + } + // Create the resource in the dataModel + if (responseWrapper.isEmpty()) { + resource = resourceBusinessLogic.createResource(resource, null, + modifier, null, null); + return buildCreatedResourceResponse(resource, context, responseWrapper); + } else { + return buildErrorResponse(responseWrapper.getInnerElement()); + } + } + + } catch (IOException|ParseException e) { + final String message = "failed to create vfc monitoring template resource"; + BeEcompErrorManager.getInstance().logBeRestApiGeneralError(message); + log.debug(message, e); + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)); + return buildErrorResponse(responseWrapper.getInnerElement()); + } catch (ComponentException e){ + ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(e); + responseWrapper.setInnerElement(responseFormat); + return buildErrorResponse(responseFormat); + } + finally { + if(AssetTypeEnum.RESOURCES.getValue().equals(assetType)) { + getComponentsUtils().auditCreateResourceExternalApi(responseWrapper.getInnerElement(), resourceCommonInfo, request, resource); + } else if(AssetTypeEnum.SERVICES.getValue().equals(assetType)) { + getComponentsUtils().auditCreateServiceExternalApi(responseWrapper.getInnerElement(), request, service); + } + } + } + + /** + * Changing the lifecycle of an asset + * @param jsonChangeInfo The description - request body + * @param assetType The requested asset type.Valid values are: resources / services (for VFCMT – use "resources") + * @param uuid The uuid of the desired resource to be changed + * @param lifecycleTransition The lifecycle operation to be performed on the asset.Valid values are:Checkin / Checkout / CERTIFICATION_REQUEST + * @param userId + * @return + */ + @POST + @Path("/{assetType}/{uuid}/lifecycleState/{lifecycleOperation}") + @Consumes(MediaType.APPLICATION_JSON) + @Produces(MediaType.APPLICATION_JSON) + @Operation(description = "Change Resource lifecycle State", method = "POST") + @ApiResponses(value = { + @ApiResponse(responseCode = "200", description = "Resource state changed", + content = @Content(array = @ArraySchema(schema = @Schema(implementation = AssetMetadata.class)))), + @ApiResponse(responseCode = "400", description = "Missing X-ECOMP-InstanceID HTTP header - POL5001"), + @ApiResponse(responseCode = "401", + description = "ECOMP component should authenticate itself and to re-send again HTTP request with its Basic Authentication credentials - POL5002"), + @ApiResponse(responseCode = "403", description = "ECOMP component is not authorized - POL5003"), + @ApiResponse(responseCode = "404", + description = "Error: Requested '%1' (uuid) resource was not found - SVC4063"), + @ApiResponse(responseCode = "405", + description = "Method Not Allowed : Invalid HTTP method type used ( PUT,DELETE,POST will be rejected) - POL4050"), + @ApiResponse(responseCode = "500", + description = "The GET request failed either due to internal SDC problem. ECOMP Component should continue the attempts to get the needed information - POL5000"), + @ApiResponse(responseCode = "403", description = "Asset is already checked-out by another user - SVC4085"), + @ApiResponse(responseCode = "403", + description = "Asset is being edited by different user. Only one user can checkout and edit an asset on given time. The asset will be available for checkout after the other user will checkin the asset - SVC4080")}) + // @ApiImplicitParams({@ApiImplicitParam(required = true, dataType = "org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction", paramType = "body", value = "userRemarks - Short description (free text) about the asset version being changed")}) + public Response changeResourceStateExternal( + @Parameter(description = "Determines the format of the body of the request", + required = true) @HeaderParam(value = Constants.CONTENT_TYPE_HEADER) String contentType, + @Parameter(description = "The user id", + required = true) @HeaderParam(value = Constants.USER_ID_HEADER) final String userId, + @Parameter(description = "X-ECOMP-RequestID header", + required = false) @HeaderParam(value = Constants.X_ECOMP_REQUEST_ID_HEADER) String requestId, + @Parameter(description = "X-ECOMP-InstanceID header", required = true) @HeaderParam( + value = Constants.X_ECOMP_INSTANCE_ID_HEADER) final String instanceIdHeader, + @Parameter(description = "Determines the format of the body of the response", + required = false) @HeaderParam(value = Constants.ACCEPT_HEADER) String accept, + @Parameter(description = "The username and password", + required = true) @HeaderParam(value = Constants.AUTHORIZATION_HEADER) String authorization, + @Parameter(schema = @Schema(allowableValues = {"checkout, checkin"}), + required = true) @PathParam(value = "lifecycleOperation") final String lifecycleTransition, + @Parameter(description = "id of component to be changed") @PathParam(value = "uuid") final String uuid, + @Parameter(description = "validValues: resources / services ", + schema = @Schema(allowableValues = {ComponentTypeEnum.RESOURCE_PARAM_NAME , + ComponentTypeEnum.SERVICE_PARAM_NAME})) @PathParam( + value = "assetType") final String assetType, + @Parameter(hidden = true) String jsonChangeInfo) { + + Response response = null; + + init(); + + String requestURI = request.getRequestURI(); + String url = request.getMethod() + " " + requestURI; + log.debug("Start handle request of {}", url); + + //get the business logic + ServletContext context = request.getSession().getServletContext(); + + Wrapper responseWrapper = runValidations(assetType); + ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType); + Component component = null; + Component responseObject = null; + User modifier = null; + + try{ + // Validate X-ECOMP-InstanceID Header + if (responseWrapper.isEmpty()) { + validateXECOMPInstanceIDHeader(instanceIdHeader, responseWrapper); + } + + if (responseWrapper.isEmpty()) { + //get user + Either eitherGetUser = getUser(request, userId); + if (eitherGetUser.isRight()) { + ResponseFormat responseFormat = eitherGetUser.right().value(); + responseWrapper.setInnerElement(responseFormat); + return buildErrorResponse(responseFormat); + } + modifier = eitherGetUser.left().value(); + + //get the component id from the uuid + Either latestVersion = lifecycleBusinessLogic.getLatestComponentByUuid(componentType, uuid); + if (latestVersion.isRight()) { + ResponseFormat responseFormat = latestVersion.right().value(); + responseWrapper.setInnerElement(responseFormat); + return buildErrorResponse(responseFormat); + } + component = latestVersion.left().value(); + String componentId = component.getUniqueId(); + + //validate the transition is valid + Either validateEnum = validateTransitionEnum(lifecycleTransition, modifier); + if (validateEnum.isRight()) { + ResponseFormat responseFormat = validateEnum.right().value(); + responseWrapper.setInnerElement(responseFormat); + return buildErrorResponse(responseFormat); + } + LifeCycleTransitionEnum transitionEnum = validateEnum.left().value(); + + //create changeInfo + LifecycleChangeInfoWithAction changeInfo = new LifecycleChangeInfoWithAction(); + try { + if (jsonChangeInfo != null && !jsonChangeInfo.isEmpty()) { + ObjectMapper mapper = new ObjectMapper(); + changeInfo = new LifecycleChangeInfoWithAction(mapper.readValue(jsonChangeInfo, LifecycleChangeInfoBase.class).getUserRemarks()); + } + } + catch (IOException e) { + BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject"); + log.debug("failed to convert from json {}", jsonChangeInfo, e); + ResponseFormat responseFormat = getComponentsUtils().getInvalidContentErrorAndAudit(modifier, componentId, AuditingActionEnum.CHECKOUT_RESOURCE); + responseWrapper.setInnerElement(responseFormat); + return buildErrorResponse(responseFormat); + } + + //execute business logic + Either actionResponse = lifecycleBusinessLogic + .changeComponentState(componentType, componentId, modifier, transitionEnum, changeInfo, false, true); + if (actionResponse.isRight()) { + log.info("failed to change resource state"); + ResponseFormat responseFormat = actionResponse.right().value(); + responseWrapper.setInnerElement(responseFormat); + return buildErrorResponse(responseFormat); + } + + log.debug("change state successful !!!"); + responseObject = actionResponse.left().value(); + response = buildCreatedResourceResponse(responseObject, context, responseWrapper); + } else { + response = buildErrorResponse(responseWrapper.getInnerElement()); + } + + return response; + } catch (IOException e) { + BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Change Lifecycle State"); + log.debug("change lifecycle state failed with exception", e); + ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR); + responseWrapper.setInnerElement(responseFormat); + return buildErrorResponse(responseFormat); + } catch (ComponentException e){ + ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(e); + responseWrapper.setInnerElement(responseFormat); + return buildErrorResponse(responseFormat); + } + finally{ + getComponentsUtils().auditChangeLifecycleAction(responseWrapper.getInnerElement(), componentType, requestId, + component, responseObject, new DistributionData(instanceIdHeader, requestURI), modifier); + } + } + + private Response buildCreatedResourceResponse(Component resource, ServletContext context, + Wrapper responseWrapper) throws IOException { + ResponseFormat responseFormat; + Response response; + Either resMetadata = assetMetadataUtils + .convertToSingleAssetMetadata(resource, request.getRequestURL().toString(), + true); + if (resMetadata.isRight()) { + log.debug("Asset conversion Failed"); + responseFormat = resMetadata.right().value(); + responseWrapper.setInnerElement(responseFormat); + response = buildErrorResponse(responseFormat); + }else{ + final AssetMetadata assetData = resMetadata.left().value(); + assetData.setToscaModelURL(null); + + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.CREATED)); + Object representation = RepresentationUtils.toRepresentation(assetData); + response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), representation); + } + return response; + } + + private void handleCategories(ServletContext context, String data, Resource resource, + Wrapper responseWrapper) { + try { + JSONParser parser = new JSONParser(); + JSONObject jsonObj = (JSONObject) parser.parse(data); + String category = (String) jsonObj.get(CategoryTypeEnum.CATEGORY.getValue()); + String subcategory = (String) jsonObj.get(CategoryTypeEnum.SUBCATEGORY.getValue()); + if (Strings.isEmpty(category)) { + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat( + ActionStatus.COMPONENT_MISSING_CATEGORY, ComponentTypeEnum.RESOURCE.getValue())); + } + else if (Strings.isEmpty(subcategory)) { + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat( + ActionStatus.COMPONENT_MISSING_SUBCATEGORY)); + } + if (responseWrapper.isEmpty()) { + // get All Categories + Either, ActionStatus> allResourceCategories = elementBusinessLogic + .getAllResourceCategories(); + // Error fetching categories + if (allResourceCategories.isRight()) { + responseWrapper.setInnerElement( + getComponentsUtils().getResponseFormat(allResourceCategories.right().value())); + } else { + addCategories(resource, category, subcategory, allResourceCategories, responseWrapper); + } + } + } catch (ParseException e) { + log.debug("Exception occured in addCategories: {}", e.getMessage(), e); + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR)); + } + + } + + private void addCategories(Resource resource, String category, String subcategory, + Either, ActionStatus> allResourceCategories, + Wrapper responseWrapper) { + Optional optionalCategory = + // Stream of all the categories + allResourceCategories.left().value().stream() + // filter in only relevant category + .filter(e -> e.getName().equals(category)) + // get the result + .findAny(); + if (!optionalCategory.isPresent()) { + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat( + ActionStatus.COMPONENT_INVALID_CATEGORY, ComponentTypeEnum.RESOURCE.getValue())); + } else { + CategoryDefinition categoryDefinition = optionalCategory.get(); + + List subCaregories = + // Stream of all sub-categories of the relevant + // category + categoryDefinition.getSubcategories().stream() + // filter in only relevant sub-category + .filter(e -> e.getName().equals(subcategory)) + // get the result + .collect(Collectors.toList()); + + if( subCaregories.isEmpty() ){ + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat( + ActionStatus.COMPONENT_INVALID_SUBCATEGORY, ComponentTypeEnum.RESOURCE.getValue())); + } + else{ + categoryDefinition.setSubcategories(subCaregories); + resource.setCategories(Arrays.asList(categoryDefinition)); + } + + } + } + + private Wrapper runValidations(final String assetType) { + Wrapper responseWrapper = new Wrapper<>(); + + // Validate X-ECOMP-InstanceID Header + if (responseWrapper.isEmpty()) { + String instanceId = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER); + validateXECOMPInstanceIDHeader(instanceId,responseWrapper); + } + // Validate USER_ID Header + if (responseWrapper.isEmpty()) { + validateHttpCspUserIdHeader(request.getHeader(Constants.USER_ID_HEADER),responseWrapper); + } + // Validate assetType + if (responseWrapper.isEmpty()) { + if( !AssetTypeEnum.RESOURCES.getValue().equals(assetType) && !AssetTypeEnum.SERVICES.getValue().equals(assetType)){ + responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.RESTRICTED_OPERATION)); + } + } + + return responseWrapper; + } + + private Either validateTransitionEnum(final String lifecycleTransition, User user) { + LifeCycleTransitionEnum transitionEnum = LifeCycleTransitionEnum.CHECKOUT; + try { + transitionEnum = LifeCycleTransitionEnum.getFromDisplayName(lifecycleTransition); + } catch (IllegalArgumentException e) { + log.info("state operation is not valid. operations allowed are: {}", LifeCycleTransitionEnum.valuesAsString(), e); + ResponseFormat error = getComponentsUtils().getInvalidContentErrorAndAudit(user, "", AuditingActionEnum.CHECKOUT_RESOURCE); + return Either.right(error); + } + return Either.left(transitionEnum); + } + +} diff --git a/catalog-be/src/main/java/org/openecomp/sdc/be/impl/ComponentsUtils.java b/catalog-be/src/main/java/org/openecomp/sdc/be/impl/ComponentsUtils.java index 3c3a3dbef7..59e4c2e78c 100644 --- a/catalog-be/src/main/java/org/openecomp/sdc/be/impl/ComponentsUtils.java +++ b/catalog-be/src/main/java/org/openecomp/sdc/be/impl/ComponentsUtils.java @@ -612,6 +612,34 @@ public class ComponentsUtils { getAuditingManager().auditEvent(factory); } + + public void auditCreateServiceExternalApi(ResponseFormat responseFormat, HttpServletRequest request, Component service) { + + String invariantUuid = null; + String serviceInstanceId = null; + + User modifier = new User(); + modifier.setUserId(request.getHeader(Constants.USER_ID_HEADER)); + DistributionData distributionData = new DistributionData(request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER), request.getRequestURI()); + String requestId = request.getHeader(Constants.X_ECOMP_REQUEST_ID_HEADER); + + if(null != service) { + invariantUuid = service.getInvariantUUID(); + serviceInstanceId = service.getUUID(); + } + + AuditEventFactory factory = new AuditCreateServiceExternalApiEventFactory( + CommonAuditData.newBuilder() + .status(responseFormat.getStatus()) + .description(getMessageString(responseFormat)) + .requestId(requestId) + .serviceInstanceId(serviceInstanceId) + .build(), + new ResourceCommonInfo(ComponentTypeEnum.SERVICE.name()), distributionData, invariantUuid, modifier); + + getAuditingManager().auditEvent(factory); + + } public void auditExternalActivateService(ResponseFormat responseFormat, DistributionData distributionData, String requestId, String serviceInstanceUuid, User modifier) { AuditEventFactory factory = new AuditActivateServiceExternalApiEventFactory( -- cgit 1.2.3-korg