2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.openecomp.sdc.be.externalapi.servlet;
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;
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;
103 import static com.google.common.base.Strings.isNullOrEmpty;
106 @Loggable(prepend = true, value = Loggable.DEBUG, trim = false)
108 @OpenAPIDefinition(info = @Info(title = "CRUD External Servlet",
109 description = "This Servlet serves external users for creating assets and changing their lifecycle state"))
112 public class CrudExternalServlet extends AbstractValidationsServlet {
115 private HttpServletRequest request;
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;
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;
143 * Creates a new Asset (Resource or Service)
148 * @param instanceIdHeader
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,
216 allowableValues = {"resources, services"})) @PathParam("assetType") final String assetType,
217 @Parameter(hidden = true) String data) {
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;
230 ServletContext context = request.getSession().getServletContext();
232 // Validate X-ECOMP-InstanceID Header
233 if (responseWrapper.isEmpty()) {
234 validateXECOMPInstanceIDHeader(instanceIdHeader, responseWrapper);
236 // Validate USER_ID Header
237 if (responseWrapper.isEmpty()) {
238 validateHttpCspUserIdHeader(userId, responseWrapper);
240 // Validate assetType
242 if( responseWrapper.isEmpty() && !(AssetTypeEnum.RESOURCES.getValue().equals(assetType) || AssetTypeEnum.SERVICES.getValue().equals(assetType))) {
243 responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
246 if (responseWrapper.isEmpty() && AssetTypeEnum.SERVICES.getValue().equals(assetType)) {
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());
257 service = convertResponse.left().value();
261 responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(
262 ActionStatus.SERVICE_NOT_FOUND, ComponentTypeEnum.SERVICE.getValue()));
265 //validate name exist
266 if(responseWrapper.isEmpty() && isNullOrEmpty(service.getName())){
267 responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(
268 ActionStatus.MISSING_COMPONENT_NAME, ComponentTypeEnum.SERVICE.getValue()));
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()));
278 if(responseWrapper.isEmpty()){
279 service.setSystemName(ValidationUtils.convertToSystemName(service.getName()));
280 log.debug("Service system name :"+service.getSystemName());
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());
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);
298 return buildErrorResponse(responseWrapper.getInnerElement());
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));
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());
325 resource = eitherResource.left().value();
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()));
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);
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);
351 return buildErrorResponse(responseWrapper.getInnerElement());
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);
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);
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
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) {
430 Response response = null;
434 String requestURI = request.getRequestURI();
435 String url = request.getMethod() + " " + requestURI;
436 log.debug("Start handle request of {}", url);
438 Wrapper<ResponseFormat> responseWrapper = runValidations(assetType);
439 ComponentTypeEnum componentType = ComponentTypeEnum.findByParamName(assetType);
440 Component component = null;
441 Component responseObject = null;
442 User modifier = null;
445 // Validate X-ECOMP-InstanceID Header
446 if (responseWrapper.isEmpty()) {
447 validateXECOMPInstanceIDHeader(instanceIdHeader, responseWrapper);
450 if (responseWrapper.isEmpty()) {
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);
458 modifier = eitherGetUser.left().value();
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);
467 component = latestVersion.left().value();
468 String componentId = component.getUniqueId();
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);
477 LifeCycleTransitionEnum transitionEnum = validateEnum.left().value();
480 LifecycleChangeInfoWithAction changeInfo = new LifecycleChangeInfoWithAction();
482 if (jsonChangeInfo != null && !jsonChangeInfo.isEmpty()) {
483 ObjectMapper mapper = new ObjectMapper();
484 changeInfo = new LifecycleChangeInfoWithAction(mapper.readValue(jsonChangeInfo, LifecycleChangeInfoBase.class).getUserRemarks());
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);
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);
504 log.debug("change state successful !!!");
505 responseObject = actionResponse.left().value();
506 response = buildCreatedResourceResponse(responseObject, responseWrapper);
508 response = buildErrorResponse(responseWrapper.getInnerElement());
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);
524 getComponentsUtils().auditChangeLifecycleAction(responseWrapper.getInnerElement(), componentType, requestId,
525 component, responseObject, new DistributionData(instanceIdHeader, requestURI), modifier);
529 private Response buildCreatedResourceResponse(Component resource,
530 Wrapper<ResponseFormat> responseWrapper) throws IOException {
531 ResponseFormat responseFormat;
533 Either<? extends AssetMetadata, ResponseFormat> resMetadata = assetMetadataUtils
534 .convertToSingleAssetMetadata(resource, request.getRequestURL().toString(),
536 if (resMetadata.isRight()) {
537 log.debug("Asset conversion Failed");
538 responseFormat = resMetadata.right().value();
539 responseWrapper.setInnerElement(responseFormat);
540 response = buildErrorResponse(responseFormat);
542 final AssetMetadata assetData = resMetadata.left().value();
543 assetData.setToscaModelURL(null);
545 responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.CREATED));
546 Object representation = RepresentationUtils.toRepresentation(assetData);
547 response = buildOkResponse(getComponentsUtils().getResponseFormat(ActionStatus.CREATED), representation);
552 private void handleCategories(String data, Resource resource,
553 Wrapper<ResponseFormat> responseWrapper) {
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()));
563 else if (isNullOrEmpty(subcategory)) {
564 responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(
565 ActionStatus.COMPONENT_MISSING_SUBCATEGORY));
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()));
576 addCategories(resource, category, subcategory, allResourceCategories, responseWrapper);
579 } catch (ParseException e) {
580 log.debug("Exception occured in addCategories: {}", e.getMessage(), e);
581 responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(ActionStatus.GENERAL_ERROR));
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))
596 if (!optionalCategory.isPresent()) {
597 responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(
598 ActionStatus.COMPONENT_INVALID_CATEGORY, ComponentTypeEnum.RESOURCE.getValue()));
600 CategoryDefinition categoryDefinition = optionalCategory.get();
602 List<SubCategoryDefinition> subCaregories =
603 // Stream of all sub-categories of the relevant
605 categoryDefinition.getSubcategories().stream()
606 // filter in only relevant sub-category
607 .filter(e -> e.getName().equals(subcategory))
609 .collect(Collectors.toList());
611 if( subCaregories.isEmpty() ){
612 responseWrapper.setInnerElement(getComponentsUtils().getResponseFormat(
613 ActionStatus.COMPONENT_INVALID_SUBCATEGORY, ComponentTypeEnum.RESOURCE.getValue()));
616 categoryDefinition.setSubcategories(subCaregories);
617 resource.setCategories(Arrays.asList(categoryDefinition));
623 private Wrapper<ResponseFormat> runValidations(final String assetType) {
624 Wrapper<ResponseFormat> responseWrapper = new Wrapper<>();
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);
631 // Validate USER_ID Header
632 if (responseWrapper.isEmpty()) {
633 validateHttpCspUserIdHeader(request.getHeader(Constants.USER_ID_HEADER),responseWrapper);
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));
642 return responseWrapper;
645 private Either<LifeCycleTransitionEnum, ResponseFormat> validateTransitionEnum(final String lifecycleTransition, User user) {
646 LifeCycleTransitionEnum transitionEnum = LifeCycleTransitionEnum.CHECKOUT;
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);
654 return Either.left(transitionEnum);