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