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