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=========================================================
19 * Modifications copyright (c) 2019 Nokia
20 * ================================================================================
23 package org.openecomp.sdc.be.components.lifecycle;
25 import com.google.common.annotations.VisibleForTesting;
26 import fj.data.Either;
27 import org.openecomp.sdc.be.catalog.enums.ChangeTypeEnum;
28 import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
29 import org.openecomp.sdc.be.components.impl.ProductBusinessLogic;
30 import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
31 import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
32 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
33 import org.openecomp.sdc.be.components.impl.exceptions.ByResponseFormatComponentException;
34 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
35 import org.openecomp.sdc.be.components.impl.version.VesionUpdateHandler;
36 import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction.LifecycleChanceActionEnum;
37 import org.openecomp.sdc.be.dao.api.ActionStatus;
38 import org.openecomp.sdc.be.dao.jsongraph.JanusGraphDao;
39 import org.openecomp.sdc.be.datatypes.components.ResourceMetadataDataDefinition;
40 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
41 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
42 import org.openecomp.sdc.be.datatypes.enums.ResourceTypeEnum;
43 import org.openecomp.sdc.be.facade.operations.CatalogOperation;
44 import org.openecomp.sdc.be.impl.ComponentsUtils;
45 import org.openecomp.sdc.be.model.Component;
46 import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
47 import org.openecomp.sdc.be.model.LifecycleStateEnum;
48 import org.openecomp.sdc.be.model.Resource;
49 import org.openecomp.sdc.be.model.User;
50 import org.openecomp.sdc.be.model.jsonjanusgraph.datamodel.ToscaElement;
51 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.NodeTemplateOperation;
52 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaElementLifecycleOperation;
53 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade;
54 import org.openecomp.sdc.be.model.jsonjanusgraph.utils.ModelConverter;
55 import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
56 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
57 import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
58 import org.openecomp.sdc.be.resources.data.auditing.model.ResourceCommonInfo;
59 import org.openecomp.sdc.be.resources.data.auditing.model.ResourceVersionInfo;
60 import org.openecomp.sdc.common.api.Constants;
61 import org.openecomp.sdc.common.log.wrappers.Logger;
62 import org.openecomp.sdc.common.util.ValidationUtils;
63 import org.openecomp.sdc.exception.ResponseFormat;
64 import org.springframework.beans.factory.annotation.Autowired;
65 import org.springframework.context.annotation.Lazy;
67 import javax.annotation.PostConstruct;
68 import java.util.HashMap;
71 @org.springframework.stereotype.Component("lifecycleBusinessLogic")
72 public class LifecycleBusinessLogic {
74 private static final String COMMENT = "comment";
77 private IGraphLockOperation graphLockOperation = null;
80 private JanusGraphDao janusGraphDao;
82 private static final Logger log = Logger.getLogger(LifecycleBusinessLogic.class);
84 @javax.annotation.Resource
85 private ComponentsUtils componentUtils;
87 @javax.annotation.Resource
88 private ToscaElementLifecycleOperation lifecycleOperation;
92 private ServiceBusinessLogic serviceBusinessLogic;
96 private ResourceBusinessLogic resourceBusinessLogic;
100 private ProductBusinessLogic productBusinessLogic;
103 ToscaOperationFacade toscaOperationFacade;
106 NodeTemplateOperation nodeTemplateOperation;
109 CatalogOperation catalogOperations;
112 VesionUpdateHandler groupUpdateHandler;
114 private Map<String, LifeCycleTransition> stateTransitions;
118 initStateOperations();
121 private void initStateOperations() {
122 stateTransitions = new HashMap<>();
124 LifeCycleTransition checkoutOp = new CheckoutTransition(componentUtils, lifecycleOperation, toscaOperationFacade,
126 stateTransitions.put(checkoutOp.getName().name(), checkoutOp);
128 UndoCheckoutTransition undoCheckoutOp = new UndoCheckoutTransition(componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao);
129 undoCheckoutOp.setCatalogOperations(catalogOperations);
130 stateTransitions.put(undoCheckoutOp.getName().name(), undoCheckoutOp);
132 LifeCycleTransition checkinOp = new CheckinTransition(componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao, groupUpdateHandler);
133 stateTransitions.put(checkinOp.getName().name(), checkinOp);
135 CertificationChangeTransition successCertification = new CertificationChangeTransition(serviceBusinessLogic, LifeCycleTransitionEnum.CERTIFY, componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao);
136 successCertification.setNodeTemplateOperation(nodeTemplateOperation);
137 stateTransitions.put(successCertification.getName().name(), successCertification);
141 Map<String, LifeCycleTransition> getStartTransition() {
142 return stateTransitions;
145 // TODO: rhalili - should use changeComponentState when possible
146 public Either<Resource, ResponseFormat> changeState(String resourceId, User modifier, LifeCycleTransitionEnum transitionEnum, LifecycleChangeInfoWithAction changeInfo, boolean inTransaction, boolean needLock) {
147 return (Either<Resource, ResponseFormat>) changeComponentState(ComponentTypeEnum.RESOURCE, resourceId, modifier, transitionEnum, changeInfo, inTransaction, needLock);
150 private boolean isComponentVFCMT(Component component, ComponentTypeEnum componentType) {
151 if (componentType.equals(ComponentTypeEnum.RESOURCE)) {
152 ResourceTypeEnum resourceType = ((ResourceMetadataDataDefinition) component.getComponentMetadataDefinition().getMetadataDataDefinition()).getResourceType();
153 if (resourceType.equals(ResourceTypeEnum.VFCMT)) {
160 public Either<? extends Component, ResponseFormat> changeComponentState(ComponentTypeEnum componentType, String componentId, User modifier, LifeCycleTransitionEnum transitionEnum, LifecycleChangeInfoWithAction changeInfo, boolean inTransaction,
163 LifeCycleTransition lifeCycleTransition = stateTransitions.get(transitionEnum.name());
164 if (lifeCycleTransition == null) {
165 log.debug("state operation is not valid. operations allowed are: {}", LifeCycleTransitionEnum.valuesAsString());
166 ResponseFormat error = componentUtils.getInvalidContentErrorAndAudit(modifier, componentId, AuditingActionEnum.CHECKOUT_RESOURCE);
167 return Either.right(error);
170 log.debug("get resource from graph");
171 ResponseFormat errorResponse;
173 Either<? extends Component, ResponseFormat> eitherResourceResponse = getComponentForChange(componentType, componentId, modifier, lifeCycleTransition, changeInfo);
174 if (eitherResourceResponse.isRight()) {
175 return eitherResourceResponse;
177 component = eitherResourceResponse.left().value();
178 String resourceCurrVersion = component.getVersion();
179 LifecycleStateEnum resourceCurrState = component.getLifecycleState();
182 if (!inTransaction && needLock) {
183 log.debug("lock component {}", componentId);
185 lockComponent(componentType, component);
186 }catch (ComponentException e){
187 errorResponse = e.getResponseFormat();
188 componentUtils.auditComponent(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(),
189 new ResourceCommonInfo(componentType.getValue()),
190 ResourceVersionInfo.newBuilder()
191 .state(resourceCurrState.name())
192 .version(resourceCurrVersion)
196 log.error("lock component {} failed", componentId);
197 return Either.right(errorResponse);
199 log.debug("after lock component {}", componentId);
202 Either<String, ResponseFormat> commentValidationResult = validateComment(changeInfo, transitionEnum);
203 if (commentValidationResult.isRight()) {
204 errorResponse = commentValidationResult.right().value();
205 componentUtils.auditComponent(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(),
206 new ResourceCommonInfo(componentType.getValue()),
207 ResourceVersionInfo.newBuilder()
208 .state(resourceCurrState.name())
209 .version(resourceCurrVersion)
211 changeInfo.getUserRemarks());
212 return Either.right(errorResponse);
214 changeInfo.setUserRemarks(commentValidationResult.left().value());
215 log.debug("after validate component");
216 Either<Boolean, ResponseFormat> validateHighestVersion = validateHighestVersion(modifier, lifeCycleTransition, component, resourceCurrVersion, componentType);
217 if (validateHighestVersion.isRight()) {
218 return Either.right(validateHighestVersion.right().value());
220 log.debug("after validate Highest Version");
221 final Component oldComponent = component;
222 Either<? extends Component, ResponseFormat> checkedInComponentEither = checkInBeforeCertifyIfNeeded(componentType, modifier, transitionEnum, changeInfo, inTransaction, component);
223 if(checkedInComponentEither.isRight()) {
224 return Either.right(checkedInComponentEither.right().value());
226 component = checkedInComponentEither.left().value();
227 return changeState(component, lifeCycleTransition, componentType, modifier, changeInfo, inTransaction)
229 .bind(c -> updateCatalog(c, oldComponent, ChangeTypeEnum.LIFECYCLE));
233 component.setUniqueId(componentId);
234 if (!inTransaction && needLock) {
235 log.info("unlock component {}", componentId);
236 NodeTypeEnum nodeType = componentType.getNodeType();
237 log.info("During change state, another component {} has been created/updated", componentId);
238 graphLockOperation.unlockComponent(componentId, nodeType);
245 private Either<Component, ResponseFormat> updateCatalog(Component component, Component oldComponent, ChangeTypeEnum changeStatus){
247 log.debug("updateCatalog start");
248 Component result = component == null? oldComponent : component;
249 if(component != null){
250 ActionStatus status = catalogOperations.updateCatalog(changeStatus,component);
251 if(status != ActionStatus.OK){
252 return Either.right( componentUtils.getResponseFormat(status));
256 return Either.left(result);
259 private Either<? extends Component, ResponseFormat> checkInBeforeCertifyIfNeeded(ComponentTypeEnum componentType, User modifier, LifeCycleTransitionEnum transitionEnum, LifecycleChangeInfoWithAction changeInfo, boolean inTransaction,
260 Component component) {
262 LifecycleStateEnum oldState = component.getLifecycleState();
263 Component updatedComponent = component;
264 log.debug("Certification request for resource {} ", component.getUniqueId());
265 if (oldState == LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT && transitionEnum == LifeCycleTransitionEnum.CERTIFY) {
266 log.debug("Resource {} is in Checkout state perform checkin", component.getUniqueId());
267 Either<? extends Component, ResponseFormat> actionResponse = changeState(component, stateTransitions.get(LifeCycleTransitionEnum.CHECKIN.name()), componentType, modifier, changeInfo, inTransaction);
268 if (actionResponse.isRight()) {
269 log.debug("Failed to check in Resource {} error {}", component.getUniqueId(), actionResponse.right().value());
271 return actionResponse;
274 return Either.left(updatedComponent);
277 private Either<? extends Component, ResponseFormat> changeState(Component component, LifeCycleTransition lifeCycleTransition, ComponentTypeEnum componentType, User modifier, LifecycleChangeInfoWithAction changeInfo, boolean inTransaction) {
278 ResponseFormat errorResponse;
280 LifecycleStateEnum oldState = component.getLifecycleState();
281 String resourceCurrVersion = component.getVersion();
282 ComponentBusinessLogic bl = getComponentBL(componentType);
284 Either<User, ResponseFormat> ownerResult = lifeCycleTransition.getComponentOwner(component, componentType);
285 if (ownerResult.isRight()) {
286 return Either.right(ownerResult.right().value());
288 User owner = ownerResult.left().value();
289 log.info("owner of resource {} is {}", component.getUniqueId(), owner.getUserId());
291 Either<Boolean, ResponseFormat> stateValidationResult = lifeCycleTransition.validateBeforeTransition(component, componentType, modifier, owner, oldState, changeInfo);
292 if (stateValidationResult.isRight()) {
293 log.error("Failed to validateBeforeTransition");
294 errorResponse = stateValidationResult.right().value();
295 componentUtils.auditComponent(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(),
296 new ResourceCommonInfo(componentType.getValue()),
297 ResourceVersionInfo.newBuilder()
298 .version(resourceCurrVersion)
299 .state(oldState.name())
301 changeInfo.getUserRemarks());
302 return Either.right(errorResponse);
305 Either<? extends Component, ResponseFormat> operationResult = lifeCycleTransition.changeState(componentType, component, bl, modifier, owner, false, inTransaction);
307 if (operationResult.isRight()) {
308 errorResponse = operationResult.right().value();
309 log.info("audit before sending error response");
310 componentUtils.auditComponentAdmin(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(), componentType,
311 ResourceVersionInfo.newBuilder()
312 .state(oldState.name())
313 .version(resourceCurrVersion)
316 return Either.right(errorResponse);
318 Component resourceAfterOperation = operationResult.left().value() == null? component: operationResult.left().value() ;
319 componentUtils.auditComponent(componentUtils.getResponseFormat(ActionStatus.OK), modifier, resourceAfterOperation,
320 lifeCycleTransition.getAuditingAction(), new ResourceCommonInfo(componentType.getValue()),
321 ResourceVersionInfo.newBuilder()
322 .state(oldState.name())
323 .version(resourceCurrVersion)
325 changeInfo.getUserRemarks());
326 return operationResult;
331 private Either<? extends Component, ResponseFormat> getComponentForChange(ComponentTypeEnum componentType, String componentId, User modifier, LifeCycleTransition lifeCycleTransition, LifecycleChangeInfoWithAction changeInfo) {
333 Either<? extends Component, StorageOperationStatus> eitherResourceResponse = toscaOperationFacade.getToscaElement(componentId);
335 ResponseFormat errorResponse;
336 if (eitherResourceResponse.isRight()) {
337 ActionStatus actionStatus = componentUtils.convertFromStorageResponse(eitherResourceResponse.right().value(), componentType);
338 errorResponse = componentUtils.getResponseFormat(actionStatus, Constants.EMPTY_STRING);
339 log.debug("audit before sending response");
340 componentUtils.auditComponent(errorResponse, modifier, lifeCycleTransition.getAuditingAction(),
341 new ResourceCommonInfo(componentId, componentType.getValue()), changeInfo.getUserRemarks());
343 return Either.right(errorResponse);
345 return Either.left(eitherResourceResponse.left().value());
348 private Either<Boolean, ResponseFormat> validateHighestVersion(User modifier, LifeCycleTransition lifeCycleTransition, Component component, String resourceCurrVersion, ComponentTypeEnum componentType) {
349 ResponseFormat errorResponse;
350 if (!component.isHighestVersion()) {
351 log.debug("Component version {} is not the last version of component {}", component.getComponentMetadataDefinition().getMetadataDataDefinition().getVersion(),
352 component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
353 errorResponse = componentUtils.getResponseFormat(ActionStatus.COMPONENT_HAS_NEWER_VERSION, component.getComponentMetadataDefinition().getMetadataDataDefinition().getName(), componentType.getValue().toLowerCase());
354 componentUtils.auditComponentAdmin(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(), componentType,
355 ResourceVersionInfo.newBuilder()
356 .state(component.getLifecycleState().name())
357 .version(resourceCurrVersion)
359 return Either.right(errorResponse);
361 return Either.left(true);
364 private Boolean lockComponent(ComponentTypeEnum componentType, Component component) {
365 NodeTypeEnum nodeType = componentType.getNodeType();
366 StorageOperationStatus lockResourceStatus = graphLockOperation.lockComponent(component.getUniqueId(), nodeType);
368 if (lockResourceStatus.equals(StorageOperationStatus.OK)) {
371 ActionStatus actionStatus = componentUtils.convertFromStorageResponse(lockResourceStatus);
372 throw new ByActionStatusComponentException(actionStatus, component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
376 private Either<String, ResponseFormat> validateComment(LifecycleChangeInfoWithAction changeInfo, LifeCycleTransitionEnum transitionEnum) {
377 String comment = changeInfo.getUserRemarks();
378 if (LifeCycleTransitionEnum.CERTIFY == transitionEnum || LifeCycleTransitionEnum.CHECKIN == transitionEnum
382 if (!ValidationUtils.validateStringNotEmpty(comment)) {
383 log.debug("user comment cannot be empty or null.");
384 ResponseFormat errorResponse = componentUtils.getResponseFormat(ActionStatus.MISSING_DATA, COMMENT);
385 return Either.right(errorResponse);
388 comment = ValidationUtils.removeNoneUtf8Chars(comment);
389 comment = ValidationUtils.removeHtmlTags(comment);
390 comment = ValidationUtils.normaliseWhitespace(comment);
391 comment = ValidationUtils.stripOctets(comment);
393 if (!ValidationUtils.validateLength(comment, ValidationUtils.COMMENT_MAX_LENGTH)) {
394 log.debug("user comment exceeds limit.");
395 return Either.right(componentUtils.getResponseFormat(ActionStatus.EXCEEDS_LIMIT, COMMENT, String.valueOf(ValidationUtils.COMMENT_MAX_LENGTH)));
397 if (!ValidationUtils.validateIsEnglish(comment)) {
398 return Either.right(componentUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
401 return Either.left(comment);
404 private ComponentBusinessLogic getComponentBL(ComponentTypeEnum componentTypeEnum) {
405 ComponentBusinessLogic businessLogic;
406 switch (componentTypeEnum) {
408 businessLogic = this.resourceBusinessLogic;
411 businessLogic = this.serviceBusinessLogic;
414 businessLogic = this.productBusinessLogic;
417 throw new IllegalArgumentException("Illegal component type:" + componentTypeEnum.getValue());
419 return businessLogic;
422 public Either<Component, ResponseFormat> getLatestComponentByUuid(ComponentTypeEnum componentTypeEnum, String uuid) {
424 Either<Component, StorageOperationStatus> latestVersionEither = toscaOperationFacade.getLatestComponentByUuid(uuid);
426 if (latestVersionEither.isRight()) {
428 return Either.right(componentUtils.getResponseFormat(componentUtils.convertFromStorageResponse(latestVersionEither.right().value(), componentTypeEnum), uuid));
431 Component latestComponent = latestVersionEither.left().value();
433 return Either.left(latestComponent);
437 * Performs Force certification. Note that a Force certification is allowed for the first certification only, as only a state and a version is promoted due a Force certification, skipping other actions required if a previous certified version
442 * @param lifecycleChangeInfo
443 * @param inTransaction
447 public Resource forceResourceCertification(Resource resource, User user, LifecycleChangeInfoWithAction lifecycleChangeInfo, boolean inTransaction, boolean needLock) {
448 Resource result = null;
449 Either<ToscaElement, StorageOperationStatus> certifyResourceRes = null;
450 if (lifecycleChangeInfo.getAction() != LifecycleChanceActionEnum.CREATE_FROM_CSAR) {
451 log.debug("Force certification is not allowed for the action {}. ", lifecycleChangeInfo.getAction());
452 throw new ByActionStatusComponentException(ActionStatus.NOT_ALLOWED);
454 if (!isFirstCertification(resource.getVersion())) {
455 log.debug("Failed to perform a force certification of resource{}. Force certification is allowed for the first certification only. ", resource.getName());
456 throw new ByActionStatusComponentException(ActionStatus.NOT_ALLOWED);
459 if (!inTransaction && needLock) {
460 log.info("lock component {}", resource.getUniqueId());
461 lockComponent(resource.getComponentType(), resource);
462 log.info("after lock component {}", resource.getUniqueId());
465 certifyResourceRes = lifecycleOperation.forceCerificationOfToscaElement(resource.getUniqueId(), user.getUserId(), user.getUserId(), resource.getVersion());
466 if (certifyResourceRes.isRight()) {
467 StorageOperationStatus status = certifyResourceRes.right().value();
468 log.debug("Failed to perform a force certification of resource {}. The status is {}. ", resource.getName(), status);
469 throw new ByResponseFormatComponentException(componentUtils.getResponseFormatByResource(componentUtils.convertFromStorageResponse(status), resource));
471 result = ModelConverter.convertFromToscaElement(certifyResourceRes.left().value());
472 resource.setMetadataDefinition(result.getComponentMetadataDefinition());
474 log.info("unlock component {}", resource.getUniqueId());
475 if (!inTransaction) {
476 if (result != null) {
477 janusGraphDao.commit();
479 janusGraphDao.rollback();
482 NodeTypeEnum nodeType = resource.getComponentType().getNodeType();
483 log.info("During change state, another component {} has been created/updated", resource.getUniqueId());
484 graphLockOperation.unlockComponent(resource.getUniqueId(), nodeType);
491 public boolean isFirstCertification(String previousVersion) {
492 return previousVersion.split("\\.")[0].equals("0");