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