6d94ff64713160c944c7f4dc0a4c1d2e05f43670
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / externalapi / servlet / CrudExternalServlet.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20 package org.openecomp.sdc.be.externalapi.servlet;
21
22 import static com.google.common.base.Strings.isNullOrEmpty;
23
24 import com.fasterxml.jackson.databind.ObjectMapper;
25 import com.jcabi.aspects.Loggable;
26 import fj.data.Either;
27 import io.swagger.v3.oas.annotations.Operation;
28 import io.swagger.v3.oas.annotations.Parameter;
29 import io.swagger.v3.oas.annotations.media.ArraySchema;
30 import io.swagger.v3.oas.annotations.media.Content;
31 import io.swagger.v3.oas.annotations.media.Schema;
32 import io.swagger.v3.oas.annotations.responses.ApiResponse;
33 import io.swagger.v3.oas.annotations.servers.Server;
34 import io.swagger.v3.oas.annotations.tags.Tag;
35 import java.io.IOException;
36 import java.util.Arrays;
37 import java.util.List;
38 import java.util.Optional;
39 import java.util.stream.Collectors;
40 import javax.inject.Inject;
41 import javax.servlet.http.HttpServletRequest;
42 import javax.ws.rs.Consumes;
43 import javax.ws.rs.HeaderParam;
44 import javax.ws.rs.POST;
45 import javax.ws.rs.Path;
46 import javax.ws.rs.PathParam;
47 import javax.ws.rs.Produces;
48 import javax.ws.rs.core.Context;
49 import javax.ws.rs.core.MediaType;
50 import javax.ws.rs.core.Response;
51 import org.apache.commons.lang3.StringUtils;
52 import org.json.simple.JSONObject;
53 import org.json.simple.parser.JSONParser;
54 import org.json.simple.parser.ParseException;
55 import org.openecomp.sdc.be.components.impl.ComponentInstanceBusinessLogic;
56 import org.openecomp.sdc.be.components.impl.ElementBusinessLogic;
57 import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
58 import org.openecomp.sdc.be.components.impl.ResourceImportManager;
59 import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
60 import org.openecomp.sdc.be.components.impl.aaf.AafPermission;
61 import org.openecomp.sdc.be.components.impl.aaf.PermissionAllowed;
62 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
63 import org.openecomp.sdc.be.components.lifecycle.LifecycleBusinessLogic;
64 import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoBase;
65 import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction;
66 import org.openecomp.sdc.be.config.BeEcompErrorManager;
67 import org.openecomp.sdc.be.dao.api.ActionStatus;
68 import org.openecomp.sdc.be.datamodel.api.CategoryTypeEnum;
69 import org.openecomp.sdc.be.datatypes.enums.AssetTypeEnum;
70 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
71 import org.openecomp.sdc.be.datatypes.enums.ExternalCategoryTypeEnum;
72 import org.openecomp.sdc.be.datatypes.enums.FilterKeyEnum;
73 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
74 import org.openecomp.sdc.be.ecomp.converters.AssetMetadataConverter;
75 import org.openecomp.sdc.be.externalapi.servlet.representation.AssetMetadata;
76 import org.openecomp.sdc.be.impl.ComponentsUtils;
77 import org.openecomp.sdc.be.impl.ServletUtils;
78 import org.openecomp.sdc.be.model.Component;
79 import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
80 import org.openecomp.sdc.be.model.Resource;
81 import org.openecomp.sdc.be.model.Service;
82 import org.openecomp.sdc.be.model.User;
83 import org.openecomp.sdc.be.model.category.CategoryDefinition;
84 import org.openecomp.sdc.be.model.category.SubCategoryDefinition;
85 import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
86 import org.openecomp.sdc.be.resources.data.auditing.model.DistributionData;
87 import org.openecomp.sdc.be.resources.data.auditing.model.ResourceCommonInfo;
88 import org.openecomp.sdc.be.servlets.AbstractValidationsServlet;
89 import org.openecomp.sdc.be.servlets.RepresentationUtils;
90 import org.openecomp.sdc.be.utils.CommonBeUtils;
91 import org.openecomp.sdc.common.api.Constants;
92 import org.openecomp.sdc.common.datastructure.Wrapper;
93 import org.openecomp.sdc.common.log.wrappers.Logger;
94 import org.openecomp.sdc.common.util.ValidationUtils;
95 import org.openecomp.sdc.exception.ResponseFormat;
96 import org.springframework.stereotype.Controller;
97
98 @Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
99 @Path("/v1/catalog")
100 @Tag(name = "SDCE-7 APIs")
101 @Server(url = "/sdc")
102 @Controller
103 public class CrudExternalServlet extends AbstractValidationsServlet {
104
105     private static final Logger log = Logger.getLogger(CrudExternalServlet.class);
106     private final ElementBusinessLogic elementBusinessLogic;
107     private final AssetMetadataConverter assetMetadataUtils;
108     private final LifecycleBusinessLogic lifecycleBusinessLogic;
109     private final ResourceBusinessLogic resourceBusinessLogic;
110     private final ServiceBusinessLogic serviceBusinessLogic;
111     @Context
112     private HttpServletRequest request;
113
114     @Inject
115     public CrudExternalServlet(ComponentInstanceBusinessLogic componentInstanceBL,
116                                ComponentsUtils componentsUtils, ServletUtils servletUtils, ResourceImportManager resourceImportManager,
117                                ElementBusinessLogic elementBusinessLogic, AssetMetadataConverter assetMetadataUtils,
118                                LifecycleBusinessLogic lifecycleBusinessLogic, ResourceBusinessLogic resourceBusinessLogic,
119                                ServiceBusinessLogic serviceBusinessLogic) {
120         super(componentInstanceBL, componentsUtils, servletUtils, resourceImportManager);
121         this.elementBusinessLogic = elementBusinessLogic;
122         this.assetMetadataUtils = assetMetadataUtils;
123         this.lifecycleBusinessLogic = lifecycleBusinessLogic;
124         this.resourceBusinessLogic = resourceBusinessLogic;
125         this.serviceBusinessLogic = serviceBusinessLogic;
126     }
127
128     /**
129      * Creates a new Asset (Resource or Service)
130      *
131      * @param assetType
132      * @param data
133      * @param userId
134      * @param instanceIdHeader
135      * @return
136      */
137     @POST
138     @Path("/{assetType}")
139     @Consumes(MediaType.APPLICATION_JSON)
140     @Produces(MediaType.APPLICATION_JSON)
141     @Operation(parameters = {
142         @Parameter(required = true, schema = @Schema(implementation = org.openecomp.sdc.be.model.Resource.class), description = "json describe the created resource")}, description = "creates an asset (resource or service)", method = "POST", summary = "Creates an asset (resource or service)", responses = {
143         @ApiResponse(responseCode = "200", description = "ECOMP component is authenticated and Asset created", content = @Content(array = @ArraySchema(schema = @Schema(implementation = Resource.class)))),
144         @ApiResponse(responseCode = "400", description = "Missing  X-ECOMP-InstanceID  HTTP header - POL5001"),
145         @ApiResponse(responseCode = "401", description = "ECOMP component  should authenticate itself  and  to  re-send  again  HTTP  request  with its Basic Authentication credentials - POL5002"),
146         @ApiResponse(responseCode = "403", description = "ECOMP component is not authorized - POL5003"),
147         @ApiResponse(responseCode = "404", description = "Error: Requested '%1' (uuid) resource was not found - SVC4063"),
148         @ApiResponse(responseCode = "405", description = "Method  Not Allowed  :  Invalid HTTP method type used ( PUT,DELETE,POST will be rejected) - POL4050"),
149         @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"),
150         @ApiResponse(responseCode = "400", description = "The name provided for the newly created resource is already in use for another resource in SDC - SVC4050"),
151         @ApiResponse(responseCode = "400", description = "Invalid field format. One of the provided fields does not comply with the field rules - SVC4126"),
152         @ApiResponse(responseCode = "400", description = "Missing request body. The post request did not contain the expected body - SVC4500"),
153         @ApiResponse(responseCode = "400", description = "The resource name is missing in the request body - SVC4062"),
154         @ApiResponse(responseCode = "400", description = "Create VFCMT request: VFCMT description has wrong format - SVC4064"),
155         @ApiResponse(responseCode = "400", description = "Create VFCMT request: VFCMT description has wrong format (exceeds limit) - SVC4065"),
156         @ApiResponse(responseCode = "400", description = "Create VFCMT request: VFCMT tags exceeds character limit - SVC4066"),
157         @ApiResponse(responseCode = "400", description = "Create VFCMT request: VFCMT vendor name exceeds character limit - SVC4067"),
158         @ApiResponse(responseCode = "400", description = "Create VFCMT request: VFCMT vendor release exceeds character limit - SVC4068"),
159         @ApiResponse(responseCode = "400", description = "Create VFCMT request: VFCMT ATT Contact has wrong format - SVC4069"),
160         @ApiResponse(responseCode = "400", description = "Create VFCMT request: VFCMT name has wrong format - SVC4070"),
161         @ApiResponse(responseCode = "400", description = "Create VFCMT request: VFCMT vendor name has wrong format - SVC4071"),
162         @ApiResponse(responseCode = "400", description = "Create VFCMT request: VFCMT vendor release has wrong format - SVC4072"),
163         @ApiResponse(responseCode = "400", description = "Create VFCMT request: VFCMT name exceeds character limit - SVC4073"),
164         @ApiResponse(responseCode = "400", description = "Invalid Content. Missing PROJECT_CODE number - SVC4129"),
165         @ApiResponse(responseCode = "409", description = "Error: %1 (Service) with name '%2' already exists. - SVC4050")})
166     @PermissionAllowed(AafPermission.PermNames.WRITE_VALUE)
167     public Response createComponentExternal(
168         @Parameter(description = "Determines the format of the body of the request", required = true) @HeaderParam(value = Constants.CONTENT_TYPE_HEADER) String contentType,
169         @Parameter(description = "The user id", required = true) @HeaderParam(value = Constants.USER_ID_HEADER) final String userId,
170         @Parameter(description = "X-ECOMP-RequestID header", required = false) @HeaderParam(value = Constants.X_ECOMP_REQUEST_ID_HEADER) String requestId,
171         @Parameter(description = "X-ECOMP-InstanceID header", required = true) @HeaderParam(value = Constants.X_ECOMP_INSTANCE_ID_HEADER) final String instanceIdHeader,
172         @Parameter(description = "Determines the format of the body of the response", required = false) @HeaderParam(value = Constants.ACCEPT_HEADER) String accept,
173         @Parameter(description = "The username and password", required = true) @HeaderParam(value = Constants.AUTHORIZATION_HEADER) String authorization,
174         @Parameter(description = "The requested asset type", required = true, schema = @Schema(allowableValues = {
175             "resources, services"})) @PathParam("assetType") final String assetType, @Parameter(hidden = true) String data) {
176         init();
177         Wrapper<ResponseFormat> responseWrapper = new Wrapper<>();
178         String requestURI = request.getRequestURI();
179         String url = request.getMethod() + " " + requestURI;
180         log.debug("Start handle request of {}", url);
181         Resource resource = null;
182         User modifier = null;
183         ResourceCommonInfo resourceCommonInfo = new ResourceCommonInfo(ComponentTypeEnum.RESOURCE.getValue());
184         Service service = null;
185         try {
186             // Validate X-ECOMP-InstanceID Header
187             if (responseWrapper.isEmpty()) {
188                 validateXECOMPInstanceIDHeader(instanceIdHeader, responseWrapper);
189             }
190             // Validate USER_ID Header
191             if (responseWrapper.isEmpty()) {
192                 validateHttpCspUserIdHeader(userId, responseWrapper);
193             }
194             // Validate assetType
195             if (responseWrapper.isEmpty() && !(AssetTypeEnum.RESOURCES.getValue().equals(assetType) || AssetTypeEnum.SERVICES.getValue()
196                 .equals(assetType))) {
197                 responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
198             }
199             if (responseWrapper.isEmpty() && AssetTypeEnum.SERVICES.getValue().equals(assetType)) {
200                 modifier = new User();
201                 modifier.setUserId(userId);
202                 Either<Service, ResponseFormat> convertResponse = getComponentsUtils()
203                     .convertJsonToObjectUsingObjectMapper(data, modifier, Service.class, null, ComponentTypeEnum.SERVICE);
204                 if (convertResponse.isRight()) {
205                     responseWrapper.setInnerElement(convertResponse.right().value());
206                 } else {
207                     service = convertResponse.left().value();
208                 }
209                 if (service == null) {
210                     responseWrapper.setInnerElement(
211                         getComponentsUtils().getResponseFormat(ActionStatus.SERVICE_NOT_FOUND, ComponentTypeEnum.SERVICE.getValue()));
212                 }
213                 //validate name exist
214                 if (responseWrapper.isEmpty() && service != null && isNullOrEmpty(service.getName())) {
215                     responseWrapper.setInnerElement(
216                         getComponentsUtils().getResponseFormat(ActionStatus.MISSING_COMPONENT_NAME, ComponentTypeEnum.SERVICE.getValue()));
217                 }
218                 //validate category
219                 if (responseWrapper.isEmpty() && service != null && service.getCategories() != null && !service.getCategories().isEmpty()
220                     && !ExternalCategoryTypeEnum.containsIgnoreCase(service.getCategories().get(0).getName())) {
221                     log.debug("Service category is not supported {}", service.getCategories().get(0).getName());
222                     responseWrapper.setInnerElement(
223                         getComponentsUtils().getResponseFormat(ActionStatus.COMPONENT_INVALID_CATEGORY, ComponentTypeEnum.SERVICE.getValue()));
224                 }
225                 if (responseWrapper.isEmpty() && service != null) {
226                     service.setSystemName(ValidationUtils.convertToSystemName(service.getName()));
227                     log.debug("Service system name :" + service.getSystemName());
228                 }
229                 if (responseWrapper.isEmpty()) {
230                     Either<Service, ResponseFormat> actionResponse = serviceBusinessLogic.createService(service, modifier);
231                     if (actionResponse.isRight()) {
232                         log.debug("Failed to create service");
233                         responseWrapper.setInnerElement(actionResponse.right().value());
234                         return buildErrorResponse(responseWrapper.getInnerElement());
235                     }
236                     // Create the service in the dataModel
237                     service = actionResponse.left().value();
238                     Object result = RepresentationUtils.toRepresentation(actionResponse.left().value());
239                     responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.CREATED));
240                     return buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), result);
241                 } else {
242                     return buildErrorResponse(responseWrapper.getInnerElement());
243                 }
244             } else {
245                 //Validate resource type
246                 if (responseWrapper.isEmpty()) {
247                     JSONParser parser = new JSONParser();
248                     JSONObject jsonObj = (JSONObject) parser.parse(data);
249                     String resourceType = (String) jsonObj.get(FilterKeyEnum.RESOURCE_TYPE.getName());
250                     if (StringUtils.isEmpty(resourceType) || !ResourceTypeEnum.containsName(resourceType)) {
251                         resourceCommonInfo.setResourceName((String) jsonObj.get("name"));
252                         responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.INVALID_CONTENT));
253                     }
254                 }
255                 // Convert the user json to a resource
256                 if (responseWrapper.isEmpty()) {
257                     modifier = new User();
258                     modifier.setUserId(userId);
259                     Either<Resource, ResponseFormat> eitherResource = getComponentsUtils()
260                         .convertJsonToObjectUsingObjectMapper(data, modifier, Resource.class, null, ComponentTypeEnum.RESOURCE);
261                     if (eitherResource.isRight()) {
262                         responseWrapper.setInnerElement(eitherResource.right().value());
263                     } else {
264                         resource = eitherResource.left().value();
265                     }
266                 }
267                 //validate name exist
268                 if (responseWrapper.isEmpty() && isNullOrEmpty(resource.getName())) {
269                     responseWrapper.setInnerElement(
270                         getComponentsUtils().getResponseFormat(ActionStatus.MISSING_COMPONENT_NAME, ComponentTypeEnum.RESOURCE.getValue()));
271                 }
272                 if (responseWrapper.isEmpty()) {
273                     resource.setDerivedFrom(Arrays.asList("tosca.nodes.Root"));
274                     resource.setSystemName(ValidationUtils.convertToSystemName(resource.getName()));
275                     resource.setToscaResourceName(CommonBeUtils.generateToscaResourceName(ResourceTypeEnum.VFCMT.name(), resource.getSystemName()));
276                     handleCategories(data, resource, responseWrapper);
277                 }
278                 // Create the resource in the dataModel
279                 if (responseWrapper.isEmpty()) {
280                     resource = resourceBusinessLogic.createResource(resource, null, modifier, null, null);
281                     return buildCreatedResourceResponse(resource, responseWrapper);
282                 } else {
283                     return buildErrorResponse(responseWrapper.getInnerElement());
284                 }
285             }
286         } catch (IOException | ParseException e) {
287             final String message = "failed to create vfc monitoring template resource";
288             BeEcompErrorManager.getInstance().logBeRestApiGeneralError(message);
289             log.debug(message, e);
290             responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
291             return buildErrorResponse(responseWrapper.getInnerElement());
292         } catch (ComponentException e) {
293             ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(e);
294             responseWrapper.setInnerElement(responseFormat);
295             return buildErrorResponse(responseFormat);
296         } finally {
297             if (AssetTypeEnum.RESOURCES.getValue().equals(assetType)) {
298                 getComponentsUtils().auditCreateResourceExternalApi(responseWrapper.getInnerElement(), resourceCommonInfo, request, resource);
299             } else if (AssetTypeEnum.SERVICES.getValue().equals(assetType)) {
300                 getComponentsUtils().auditCreateServiceExternalApi(responseWrapper.getInnerElement(), request, service);
301             }
302         }
303     }
304
305     /**
306      * Changing the lifecycle of an asset
307      *
308      * @param jsonChangeInfo      The description - request body
309      * @param assetType           The requested asset type.Valid values are: resources / services (for VFCMT â€“ use "resources")
310      * @param uuid                The uuid of the desired resource to be changed
311      * @param lifecycleTransition The lifecycle operation to be performed on the asset.Valid values are:Checkin / Checkout /  CERTIFICATION_REQUEST
312      * @param userId
313      * @return
314      */
315     @POST
316     @Path("/{assetType}/{uuid}/lifecycleState/{lifecycleOperation}")
317     @Consumes(MediaType.APPLICATION_JSON)
318     @Produces(MediaType.APPLICATION_JSON)
319     @Operation(parameters = {
320         @Parameter(required = true, schema = @Schema(implementation = org.openecomp.sdc.be.model.Resource.class), description = "json describe the created resource")}, description = "Change Resource lifecycle State", method = "POST", responses = {
321         @ApiResponse(responseCode = "200", description = "Resource state changed", content = @Content(array = @ArraySchema(schema = @Schema(implementation = AssetMetadata.class)))),
322         @ApiResponse(responseCode = "400", description = "Missing X-ECOMP-InstanceID HTTP header - POL5001"),
323         @ApiResponse(responseCode = "401", description = "ECOMP component  should authenticate itself  and  to  re-send  again  HTTP  request  with its Basic Authentication credentials - POL5002"),
324         @ApiResponse(responseCode = "403", description = "ECOMP component is not authorized - POL5003"),
325         @ApiResponse(responseCode = "404", description = "Error: Requested '%1' (uuid) resource was not found - SVC4063"),
326         @ApiResponse(responseCode = "405", description = "Method  Not Allowed  :  Invalid HTTP method type used ( PUT,DELETE,POST will be rejected) - POL4050"),
327         @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"),
328         @ApiResponse(responseCode = "403", description = "Asset is already checked-out by another user - SVC4085"),
329         @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")})
330     @PermissionAllowed(AafPermission.PermNames.WRITE_VALUE)
331     public Response changeResourceStateExternal(
332         @Parameter(description = "Determines the format of the body of the request", required = true) @HeaderParam(value = Constants.CONTENT_TYPE_HEADER) String contentType,
333         @Parameter(description = "The user id", required = true) @HeaderParam(value = Constants.USER_ID_HEADER) final String userId,
334         @Parameter(description = "X-ECOMP-RequestID header", required = false) @HeaderParam(value = Constants.X_ECOMP_REQUEST_ID_HEADER) String requestId,
335         @Parameter(description = "X-ECOMP-InstanceID header", required = true) @HeaderParam(value = Constants.X_ECOMP_INSTANCE_ID_HEADER) final String instanceIdHeader,
336         @Parameter(description = "Determines the format of the body of the response", required = false) @HeaderParam(value = Constants.ACCEPT_HEADER) String accept,
337         @Parameter(description = "The username and password", required = true) @HeaderParam(value = Constants.AUTHORIZATION_HEADER) String authorization,
338         @Parameter(schema = @Schema(allowableValues = {
339             "checkout, checkin"}), required = true) @PathParam(value = "lifecycleOperation") final String lifecycleTransition,
340         @Parameter(description = "id of component to be changed") @PathParam(value = "uuid") final String uuid,
341         @Parameter(description = "validValues: resources / services ", schema = @Schema(allowableValues = {ComponentTypeEnum.RESOURCE_PARAM_NAME,
342             ComponentTypeEnum.SERVICE_PARAM_NAME})) @PathParam(value = "assetType") final String assetType,
343         @Parameter(hidden = true) String jsonChangeInfo) {
344         Response response = null;
345         init();
346         String requestURI = request.getRequestURI();
347         String url = request.getMethod() + " " + requestURI;
348         log.debug("Start handle request of {}", url);
349         Wrapper<ResponseFormat> responseWrapper = runValidations(assetType);
350         ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType);
351         Component component = null;
352         Component responseObject = null;
353         User modifier = null;
354         try {
355             // Validate X-ECOMP-InstanceID Header
356             if (responseWrapper.isEmpty()) {
357                 validateXECOMPInstanceIDHeader(instanceIdHeader, responseWrapper);
358             }
359             if (responseWrapper.isEmpty()) {
360                 //get user
361                 Either<User, ResponseFormat> eitherGetUser = getUser(request, userId);
362                 if (eitherGetUser.isRight()) {
363                     ResponseFormat responseFormat = eitherGetUser.right().value();
364                     responseWrapper.setInnerElement(responseFormat);
365                     return buildErrorResponse(responseFormat);
366                 }
367                 modifier = eitherGetUser.left().value();
368                 //get the component id from the uuid
369                 Either<Component, ResponseFormat> latestVersion = lifecycleBusinessLogic.getLatestComponentByUuid(componentType, uuid);
370                 if (latestVersion.isRight()) {
371                     ResponseFormat responseFormat = latestVersion.right().value();
372                     responseWrapper.setInnerElement(responseFormat);
373                     return buildErrorResponse(responseFormat);
374                 }
375                 component = latestVersion.left().value();
376                 String componentId = component.getUniqueId();
377                 //validate the transition is valid
378                 Either<LifeCycleTransitionEnum, ResponseFormat> validateEnum = validateTransitionEnum(lifecycleTransition, modifier);
379                 if (validateEnum.isRight()) {
380                     ResponseFormat responseFormat = validateEnum.right().value();
381                     responseWrapper.setInnerElement(responseFormat);
382                     return buildErrorResponse(responseFormat);
383                 }
384                 LifeCycleTransitionEnum transitionEnum = validateEnum.left().value();
385                 //create changeInfo
386                 LifecycleChangeInfoWithAction changeInfo = new LifecycleChangeInfoWithAction();
387                 try {
388                     if (jsonChangeInfo != null && !jsonChangeInfo.isEmpty()) {
389                         ObjectMapper mapper = new ObjectMapper();
390                         changeInfo = new LifecycleChangeInfoWithAction(
391                             mapper.readValue(jsonChangeInfo, LifecycleChangeInfoBase.class).getUserRemarks());
392                     }
393                 } catch (IOException e) {
394                     BeEcompErrorManager.getInstance().logBeInvalidJsonInput("convertJsonToObject");
395                     log.debug("failed to convert from json {}", jsonChangeInfo, e);
396                     ResponseFormat responseFormat = getComponentsUtils()
397                         .getInvalidContentErrorAndAudit(modifier, componentId, AuditingActionEnum.CHECKOUT_RESOURCE);
398                     responseWrapper.setInnerElement(responseFormat);
399                     return buildErrorResponse(responseFormat);
400                 }
401                 //execute business logic
402                 Either<? extends Component, ResponseFormat> actionResponse = lifecycleBusinessLogic
403                     .changeComponentState(componentType, componentId, modifier, transitionEnum, changeInfo, false, true);
404                 if (actionResponse.isRight()) {
405                     log.info("failed to change resource state");
406                     ResponseFormat responseFormat = actionResponse.right().value();
407                     responseWrapper.setInnerElement(responseFormat);
408                     return buildErrorResponse(responseFormat);
409                 }
410                 log.debug("change state successful !!!");
411                 responseObject = actionResponse.left().value();
412                 response = buildCreatedResourceResponse(responseObject, responseWrapper);
413             } else {
414                 response = buildErrorResponse(responseWrapper.getInnerElement());
415             }
416             return response;
417         } catch (IOException e) {
418             BeEcompErrorManager.getInstance().logBeRestApiGeneralError("Change Lifecycle State");
419             log.debug("change lifecycle state failed with exception", e);
420             ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR);
421             responseWrapper.setInnerElement(responseFormat);
422             return buildErrorResponse(responseFormat);
423         } catch (ComponentException e) {
424             ResponseFormat responseFormat = getComponentsUtils().getResponseFormat(e);
425             responseWrapper.setInnerElement(responseFormat);
426             return buildErrorResponse(responseFormat);
427         } finally {
428             getComponentsUtils().auditChangeLifecycleAction(responseWrapper.getInnerElement(), componentType, requestId, component, responseObject,
429                 new DistributionData(instanceIdHeader, requestURI), modifier);
430         }
431     }
432
433     private Response buildCreatedResourceResponse(Component resource, Wrapper<ResponseFormat> responseWrapper) throws IOException {
434         ResponseFormat responseFormat;
435         Response response;
436         Either<? extends AssetMetadata, ResponseFormat> resMetadata = assetMetadataUtils
437             .convertToSingleAssetMetadata(resource, request.getRequestURL().toString(), true, null);
438         if (resMetadata.isRight()) {
439             log.debug("Asset conversion Failed");
440             responseFormat = resMetadata.right().value();
441             responseWrapper.setInnerElement(responseFormat);
442             response = buildErrorResponse(responseFormat);
443         } else {
444             final AssetMetadata assetData = resMetadata.left().value();
445             assetData.setToscaModelURL(null);
446             responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.CREATED));
447             Object representation = RepresentationUtils.toRepresentation(assetData);
448             response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), representation);
449         }
450         return response;
451     }
452
453     private void handleCategories(String data, Resource resource, Wrapper<ResponseFormat> responseWrapper) {
454         try {
455             JSONParser parser = new JSONParser();
456             JSONObject jsonObj = (JSONObject) parser.parse(data);
457             String category = (String) jsonObj.get(CategoryTypeEnum.CATEGORY.getValue());
458             String subcategory = (String) jsonObj.get(CategoryTypeEnum.SUBCATEGORY.getValue());
459             if (isNullOrEmpty(category)) {
460                 responseWrapper.setInnerElement(
461                     getComponentsUtils().getResponseFormat(ActionStatus.COMPONENT_MISSING_CATEGORY, ComponentTypeEnum.RESOURCE.getValue()));
462             } else if (isNullOrEmpty(subcategory)) {
463                 responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.COMPONENT_MISSING_SUBCATEGORY));
464             }
465             if (responseWrapper.isEmpty()) {
466                 // get All Categories
467                 Either<List<CategoryDefinition>, ActionStatus> allResourceCategories = elementBusinessLogic.getAllResourceCategories();
468                 // Error fetching categories
469                 if (allResourceCategories.isRight()) {
470                     responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(allResourceCategories.right().value()));
471                 } else {
472                     addCategories(resource, category, subcategory, allResourceCategories, responseWrapper);
473                 }
474             }
475         } catch (ParseException e) {
476             log.debug("Exception occured in addCategories: {}", e.getMessage(), e);
477             responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
478         }
479     }
480
481     private void addCategories(Resource resource, String category, String subcategory,
482                                Either<List<CategoryDefinition>, ActionStatus> allResourceCategories, Wrapper<ResponseFormat> responseWrapper) {
483         Optional<CategoryDefinition> optionalCategory =
484             // Stream of all the categories
485             allResourceCategories.left().value().stream()
486                 // filter in only relevant category
487                 .filter(e -> e.getName().equals(category))
488                 // get the result
489                 .findAny();
490         if (!optionalCategory.isPresent()) {
491             responseWrapper.setInnerElement(
492                 getComponentsUtils().getResponseFormat(ActionStatus.COMPONENT_INVALID_CATEGORY, ComponentTypeEnum.RESOURCE.getValue()));
493         } else {
494             CategoryDefinition categoryDefinition = optionalCategory.get();
495             List<SubCategoryDefinition> subCaregories =
496                 // Stream of all sub-categories of the relevant
497
498                 // category
499                 categoryDefinition.getSubcategories().stream()
500                     // filter in only relevant sub-category
501                     .filter(e -> e.getName().equals(subcategory))
502                     // get the result
503                     .collect(Collectors.toList());
504             if (subCaregories.isEmpty()) {
505                 responseWrapper.setInnerElement(
506                     getComponentsUtils().getResponseFormat(ActionStatus.COMPONENT_INVALID_SUBCATEGORY, ComponentTypeEnum.RESOURCE.getValue()));
507             } else {
508                 categoryDefinition.setSubcategories(subCaregories);
509                 resource.setCategories(Arrays.asList(categoryDefinition));
510             }
511         }
512     }
513
514     private Wrapper<ResponseFormat> runValidations(final String assetType) {
515         Wrapper<ResponseFormat> responseWrapper = new Wrapper<>();
516         // Validate X-ECOMP-InstanceID Header
517         if (responseWrapper.isEmpty()) {
518             String instanceId = request.getHeader(Constants.X_ECOMP_INSTANCE_ID_HEADER);
519             validateXECOMPInstanceIDHeader(instanceId, responseWrapper);
520         }
521         // Validate USER_ID Header
522         if (responseWrapper.isEmpty()) {
523             validateHttpCspUserIdHeader(request.getHeader(Constants.USER_ID_HEADER), responseWrapper);
524         }
525         // Validate assetType
526         if (responseWrapper.isEmpty() && !AssetTypeEnum.RESOURCES.getValue().equals(assetType) && !AssetTypeEnum.SERVICES.getValue()
527             .equals(assetType)) {
528             responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
529         }
530         return responseWrapper;
531     }
532
533     private Either<LifeCycleTransitionEnum, ResponseFormat> validateTransitionEnum(final String lifecycleTransition, User user) {
534         try {
535             return Either.left(LifeCycleTransitionEnum.getFromDisplayName(lifecycleTransition));
536         } catch (IllegalArgumentException e) {
537             log.info("state operation is not valid. operations allowed are: {}", LifeCycleTransitionEnum.valuesAsString(), e);
538             ResponseFormat error = getComponentsUtils().getInvalidContentErrorAndAudit(user, "", AuditingActionEnum.CHECKOUT_RESOURCE);
539             return Either.right(error);
540         }
541     }
542 }