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