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 * ================================================================================
22 package org.openecomp.sdc.be.components.lifecycle;
24 import com.google.common.annotations.VisibleForTesting;
25 import fj.data.Either;
26 import java.util.HashMap;
28 import javax.annotation.PostConstruct;
29 import org.apache.commons.lang3.StringUtils;
30 import org.openecomp.sdc.be.catalog.enums.ChangeTypeEnum;
31 import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
32 import org.openecomp.sdc.be.components.impl.ProductBusinessLogic;
33 import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
34 import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
35 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
36 import org.openecomp.sdc.be.components.impl.exceptions.ByResponseFormatComponentException;
37 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
38 import org.openecomp.sdc.be.components.impl.version.VesionUpdateHandler;
39 import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction.LifecycleChanceActionEnum;
40 import org.openecomp.sdc.be.dao.api.ActionStatus;
41 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphDao;
42 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
43 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
44 import org.openecomp.sdc.be.facade.operations.CatalogOperation;
45 import org.openecomp.sdc.be.impl.ComponentsUtils;
46 import org.openecomp.sdc.be.model.Component;
47 import org.openecomp.sdc.be.model.LifeCycleTransitionEnum;
48 import org.openecomp.sdc.be.model.LifecycleStateEnum;
49 import org.openecomp.sdc.be.model.Resource;
50 import org.openecomp.sdc.be.model.User;
51 import org.openecomp.sdc.be.model.jsonjanusgraph.datamodel.ToscaElement;
52 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.NodeTemplateOperation;
53 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaElementLifecycleOperation;
54 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ToscaOperationFacade;
55 import org.openecomp.sdc.be.model.jsonjanusgraph.utils.ModelConverter;
56 import org.openecomp.sdc.be.model.operations.api.IGraphLockOperation;
57 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
58 import org.openecomp.sdc.be.resources.data.auditing.AuditingActionEnum;
59 import org.openecomp.sdc.be.resources.data.auditing.model.ResourceCommonInfo;
60 import org.openecomp.sdc.be.resources.data.auditing.model.ResourceVersionInfo;
61 import org.openecomp.sdc.common.api.Constants;
62 import org.openecomp.sdc.common.log.wrappers.Logger;
63 import org.openecomp.sdc.common.util.ValidationUtils;
64 import org.openecomp.sdc.exception.ResponseFormat;
65 import org.springframework.beans.factory.annotation.Autowired;
66 import org.springframework.context.annotation.Lazy;
68 @org.springframework.stereotype.Component("lifecycleBusinessLogic")
69 public class LifecycleBusinessLogic {
71 private static final String COMMENT = "comment";
72 private static final Logger log = Logger.getLogger(LifecycleBusinessLogic.class);
74 ToscaOperationFacade toscaOperationFacade;
76 NodeTemplateOperation nodeTemplateOperation;
78 CatalogOperation catalogOperations;
80 VesionUpdateHandler groupUpdateHandler;
82 private IGraphLockOperation graphLockOperation = null;
84 private JanusGraphDao janusGraphDao;
85 @javax.annotation.Resource
86 private ComponentsUtils componentUtils;
87 @javax.annotation.Resource
88 private ToscaElementLifecycleOperation lifecycleOperation;
91 private ServiceBusinessLogic serviceBusinessLogic;
94 private ResourceBusinessLogic resourceBusinessLogic;
97 private ProductBusinessLogic productBusinessLogic;
98 private Map<String, LifeCycleTransition> stateTransitions;
102 initStateOperations();
105 private void initStateOperations() {
106 stateTransitions = new HashMap<>();
107 LifeCycleTransition checkoutOp = new CheckoutTransition(componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao);
108 stateTransitions.put(checkoutOp.getName().name(), checkoutOp);
109 UndoCheckoutTransition undoCheckoutOp = new UndoCheckoutTransition(componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao);
110 undoCheckoutOp.setCatalogOperations(catalogOperations);
111 stateTransitions.put(undoCheckoutOp.getName().name(), undoCheckoutOp);
112 LifeCycleTransition checkinOp = new CheckinTransition(componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao,
114 stateTransitions.put(checkinOp.getName().name(), checkinOp);
115 CertificationChangeTransition successCertification = new CertificationChangeTransition(serviceBusinessLogic, LifeCycleTransitionEnum.CERTIFY,
116 componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao);
117 successCertification.setNodeTemplateOperation(nodeTemplateOperation);
118 stateTransitions.put(successCertification.getName().name(), successCertification);
122 Map<String, LifeCycleTransition> getStartTransition() {
123 return stateTransitions;
126 // TODO: rhalili - should use changeComponentState when possible
127 public Either<Resource, ResponseFormat> changeState(String resourceId, User modifier, LifeCycleTransitionEnum transitionEnum,
128 LifecycleChangeInfoWithAction changeInfo, boolean inTransaction, boolean needLock) {
129 return changeComponentState(ComponentTypeEnum.RESOURCE, resourceId, modifier, transitionEnum, changeInfo, inTransaction, needLock);
132 public <T extends Component> Either<T, ResponseFormat> changeComponentState(ComponentTypeEnum componentType, String componentId, User modifier,
133 LifeCycleTransitionEnum transitionEnum,
134 LifecycleChangeInfoWithAction changeInfo, boolean inTransaction,
136 LifeCycleTransition lifeCycleTransition = stateTransitions.get(transitionEnum.name());
137 if (lifeCycleTransition == null) {
138 log.debug("state operation is not valid. operations allowed are: {}", LifeCycleTransitionEnum.valuesAsString());
139 ResponseFormat error = componentUtils.getInvalidContentErrorAndAudit(modifier, componentId, AuditingActionEnum.CHECKOUT_RESOURCE);
140 return Either.right(error);
142 log.debug("get resource from graph");
143 ResponseFormat errorResponse;
144 Either<T, ResponseFormat> eitherResourceResponse = getComponentForChange(componentType, componentId, modifier, lifeCycleTransition,
146 if (eitherResourceResponse.isRight()) {
147 return eitherResourceResponse;
149 T component = eitherResourceResponse.left().value();
150 String resourceCurrVersion = component.getVersion();
151 LifecycleStateEnum resourceCurrState = component.getLifecycleState();
153 if (!inTransaction && needLock) {
154 log.debug("lock component {}", componentId);
156 lockComponent(componentType, component);
157 } catch (ComponentException e) {
158 errorResponse = e.getResponseFormat();
159 componentUtils.auditComponent(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(),
160 new ResourceCommonInfo(componentType.getValue()),
161 ResourceVersionInfo.newBuilder().state(resourceCurrState.name()).version(resourceCurrVersion).build());
162 log.error("lock component {} failed", componentId);
163 return Either.right(errorResponse);
165 log.debug("after lock component {}", componentId);
168 Either<String, ResponseFormat> commentValidationResult = validateComment(changeInfo, transitionEnum);
169 if (commentValidationResult.isRight()) {
170 errorResponse = commentValidationResult.right().value();
171 componentUtils.auditComponent(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(),
172 new ResourceCommonInfo(componentType.getValue()),
173 ResourceVersionInfo.newBuilder().state(resourceCurrState.name()).version(resourceCurrVersion).build(),
174 changeInfo.getUserRemarks());
175 return Either.right(errorResponse);
177 changeInfo.setUserRemarks(commentValidationResult.left().value());
178 log.debug("after validate component");
179 Either<Boolean, ResponseFormat> validateHighestVersion = validateHighestVersion(modifier, lifeCycleTransition, component,
180 resourceCurrVersion, componentType);
181 if (validateHighestVersion.isRight()) {
182 return Either.right(validateHighestVersion.right().value());
184 log.debug("after validate Highest Version");
185 final T oldComponent = component;
186 Either<T, ResponseFormat> checkedInComponentEither = checkInBeforeCertifyIfNeeded(componentType, modifier, transitionEnum, changeInfo,
187 inTransaction, component);
188 if (checkedInComponentEither.isRight()) {
189 return Either.right(checkedInComponentEither.right().value());
191 component = checkedInComponentEither.left().value();
192 return changeState(component, lifeCycleTransition, componentType, modifier, changeInfo, inTransaction).left()
193 .bind(c -> updateCatalog(c, oldComponent, ChangeTypeEnum.LIFECYCLE));
195 component.setUniqueId(componentId);
196 if (!inTransaction && needLock) {
197 log.info("unlock component {}", componentId);
198 NodeTypeEnum nodeType = componentType.getNodeType();
199 log.info("During change state, another component {} has been created/updated", componentId);
200 graphLockOperation.unlockComponent(componentId, nodeType);
205 private <T extends Component> Either<T, ResponseFormat> updateCatalog(T component, T oldComponent, ChangeTypeEnum changeStatus) {
206 log.debug("updateCatalog start");
207 T result = component == null ? oldComponent : component;
208 if (component != null) {
209 ActionStatus status = catalogOperations.updateCatalog(changeStatus, component);
210 if (status != ActionStatus.OK) {
211 return Either.right(componentUtils.getResponseFormat(status));
214 return Either.left(result);
217 private <T extends Component> Either<T, ResponseFormat> checkInBeforeCertifyIfNeeded(ComponentTypeEnum componentType, User modifier,
218 LifeCycleTransitionEnum transitionEnum,
219 LifecycleChangeInfoWithAction changeInfo,
220 boolean inTransaction, T component) {
221 LifecycleStateEnum oldState = component.getLifecycleState();
222 log.debug("Certification request for resource {} ", component.getUniqueId());
223 if (oldState == LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT && transitionEnum == LifeCycleTransitionEnum.CERTIFY) {
224 log.debug("Resource {} is in Checkout state perform checkin", component.getUniqueId());
225 Either<T, ResponseFormat> actionResponse = changeState(component, stateTransitions.get(LifeCycleTransitionEnum.CHECKIN.name()),
226 componentType, modifier, changeInfo, inTransaction);
227 if (actionResponse.isRight()) {
228 log.debug("Failed to check in Resource {} error {}", component.getUniqueId(), actionResponse.right().value());
230 return actionResponse;
232 return Either.left(component);
235 private <T extends Component> Either<T, ResponseFormat> changeState(T component, LifeCycleTransition lifeCycleTransition,
236 ComponentTypeEnum componentType, User modifier,
237 LifecycleChangeInfoWithAction changeInfo, boolean inTransaction) {
238 ResponseFormat errorResponse;
239 LifecycleStateEnum oldState = component.getLifecycleState();
240 String resourceCurrVersion = component.getVersion();
241 ComponentBusinessLogic bl = getComponentBL(componentType);
242 Either<User, ResponseFormat> ownerResult = lifeCycleTransition.getComponentOwner(component, componentType);
243 if (ownerResult.isRight()) {
244 return Either.right(ownerResult.right().value());
246 User owner = ownerResult.left().value();
247 log.info("owner of resource {} is {}", component.getUniqueId(), owner.getUserId());
248 Either<Boolean, ResponseFormat> stateValidationResult = lifeCycleTransition
249 .validateBeforeTransition(component, componentType, modifier, owner, oldState, changeInfo);
250 if (stateValidationResult.isRight()) {
251 log.error("Failed to validateBeforeTransition");
252 errorResponse = stateValidationResult.right().value();
253 componentUtils.auditComponent(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(),
254 new ResourceCommonInfo(componentType.getValue()),
255 ResourceVersionInfo.newBuilder().version(resourceCurrVersion).state(oldState.name()).build(), changeInfo.getUserRemarks());
256 return Either.right(errorResponse);
258 Either<T, ResponseFormat> operationResult = lifeCycleTransition
259 .changeState(componentType, component, bl, modifier, owner, false, inTransaction);
260 if (operationResult.isRight()) {
261 errorResponse = operationResult.right().value();
262 log.info("audit before sending error response");
263 componentUtils.auditComponentAdmin(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(), componentType,
264 ResourceVersionInfo.newBuilder().state(oldState.name()).version(resourceCurrVersion).build());
265 return Either.right(errorResponse);
267 Component resourceAfterOperation = operationResult.left().value() == null ? component : operationResult.left().value();
268 componentUtils.auditComponent(componentUtils.getResponseFormat(ActionStatus.OK), modifier, resourceAfterOperation,
269 lifeCycleTransition.getAuditingAction(), new ResourceCommonInfo(componentType.getValue()),
270 ResourceVersionInfo.newBuilder().state(oldState.name()).version(resourceCurrVersion).build(), changeInfo.getUserRemarks());
271 return operationResult;
274 private <T extends Component> Either<T, ResponseFormat> getComponentForChange(ComponentTypeEnum componentType, String componentId, User modifier,
275 LifeCycleTransition lifeCycleTransition,
276 LifecycleChangeInfoWithAction changeInfo) {
277 Either<T, StorageOperationStatus> eitherResourceResponse = toscaOperationFacade.getToscaElement(componentId);
278 ResponseFormat errorResponse;
279 if (eitherResourceResponse.isRight()) {
280 ActionStatus actionStatus = componentUtils.convertFromStorageResponse(eitherResourceResponse.right().value(), componentType);
281 errorResponse = componentUtils.getResponseFormat(actionStatus, Constants.EMPTY_STRING);
282 log.debug("audit before sending response");
283 componentUtils.auditComponent(errorResponse, modifier, lifeCycleTransition.getAuditingAction(),
284 new ResourceCommonInfo(componentId, componentType.getValue()), changeInfo.getUserRemarks());
285 return Either.right(errorResponse);
287 return Either.left(eitherResourceResponse.left().value());
290 private Either<Boolean, ResponseFormat> validateHighestVersion(User modifier, LifeCycleTransition lifeCycleTransition, Component component,
291 String resourceCurrVersion, ComponentTypeEnum componentType) {
292 ResponseFormat errorResponse;
293 if (!component.isHighestVersion()) {
294 log.debug("Component version {} is not the last version of component {}",
295 component.getComponentMetadataDefinition().getMetadataDataDefinition().getVersion(),
296 component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
297 errorResponse = componentUtils.getResponseFormat(ActionStatus.COMPONENT_HAS_NEWER_VERSION,
298 component.getComponentMetadataDefinition().getMetadataDataDefinition().getName(), componentType.getValue().toLowerCase());
299 componentUtils.auditComponentAdmin(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(), componentType,
300 ResourceVersionInfo.newBuilder().state(component.getLifecycleState().name()).version(resourceCurrVersion).build());
301 return Either.right(errorResponse);
303 return Either.left(true);
306 private Boolean lockComponent(ComponentTypeEnum componentType, Component component) {
307 NodeTypeEnum nodeType = componentType.getNodeType();
308 StorageOperationStatus lockResourceStatus = graphLockOperation.lockComponent(component.getUniqueId(), nodeType);
309 if (lockResourceStatus.equals(StorageOperationStatus.OK)) {
312 ActionStatus actionStatus = componentUtils.convertFromStorageResponse(lockResourceStatus);
313 throw new ByActionStatusComponentException(actionStatus,
314 component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
318 private Either<String, ResponseFormat> validateComment(LifecycleChangeInfoWithAction changeInfo, LifeCycleTransitionEnum transitionEnum) {
319 String comment = changeInfo.getUserRemarks();
320 if (LifeCycleTransitionEnum.CERTIFY == transitionEnum || LifeCycleTransitionEnum.CHECKIN == transitionEnum
323 if (StringUtils.isEmpty(comment)) {
324 log.debug("user comment cannot be empty or null.");
325 ResponseFormat errorResponse = componentUtils.getResponseFormat(ActionStatus.MISSING_DATA, COMMENT);
326 return Either.right(errorResponse);
328 comment = ValidationUtils.removeNoneUtf8Chars(comment);
329 comment = ValidationUtils.removeHtmlTags(comment);
330 comment = ValidationUtils.normaliseWhitespace(comment);
331 comment = ValidationUtils.stripOctets(comment);
332 if (!ValidationUtils.validateLength(comment, ValidationUtils.COMMENT_MAX_LENGTH)) {
333 log.debug("user comment exceeds limit.");
335 .right(componentUtils.getResponseFormat(ActionStatus.EXCEEDS_LIMIT, COMMENT, String.valueOf(ValidationUtils.COMMENT_MAX_LENGTH)));
337 if (!ValidationUtils.validateIsEnglish(comment)) {
338 return Either.right(componentUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
341 return Either.left(comment);
344 private ComponentBusinessLogic getComponentBL(ComponentTypeEnum componentTypeEnum) {
345 ComponentBusinessLogic businessLogic;
346 switch (componentTypeEnum) {
348 businessLogic = this.resourceBusinessLogic;
351 businessLogic = this.serviceBusinessLogic;
354 businessLogic = this.productBusinessLogic;
357 throw new IllegalArgumentException("Illegal component type:" + componentTypeEnum.getValue());
359 return businessLogic;
362 public Either<Component, ResponseFormat> getLatestComponentByUuid(ComponentTypeEnum componentTypeEnum, String uuid) {
363 Either<Component, StorageOperationStatus> latestVersionEither = toscaOperationFacade.getLatestComponentByUuid(uuid);
364 if (latestVersionEither.isRight()) {
365 return Either.right(componentUtils
366 .getResponseFormat(componentUtils.convertFromStorageResponse(latestVersionEither.right().value(), componentTypeEnum), uuid));
368 Component latestComponent = latestVersionEither.left().value();
369 return Either.left(latestComponent);
373 * Performs Force certification. Note that a Force certification is allowed for the first certification only, as only a state and a version is
374 * promoted due a Force certification, skipping other actions required if a previous certified version exists.
378 * @param lifecycleChangeInfo
379 * @param inTransaction
383 public Resource forceResourceCertification(Resource resource, User user, LifecycleChangeInfoWithAction lifecycleChangeInfo, boolean inTransaction,
385 Resource result = null;
386 Either<ToscaElement, StorageOperationStatus> certifyResourceRes = null;
387 if (lifecycleChangeInfo.getAction() != LifecycleChanceActionEnum.CREATE_FROM_CSAR) {
388 log.debug("Force certification is not allowed for the action {}. ", lifecycleChangeInfo.getAction());
389 throw new ByActionStatusComponentException(ActionStatus.NOT_ALLOWED);
391 if (!isFirstCertification(resource.getVersion())) {
392 log.debug("Failed to perform a force certification of resource{}. Force certification is allowed for the first certification only. ",
394 throw new ByActionStatusComponentException(ActionStatus.NOT_ALLOWED);
397 if (!inTransaction && needLock) {
398 log.info("lock component {}", resource.getUniqueId());
399 lockComponent(resource.getComponentType(), resource);
400 log.info("after lock component {}", resource.getUniqueId());
403 certifyResourceRes = lifecycleOperation
404 .forceCerificationOfToscaElement(resource.getUniqueId(), user.getUserId(), user.getUserId(), resource.getVersion());
405 if (certifyResourceRes.isRight()) {
406 StorageOperationStatus status = certifyResourceRes.right().value();
407 log.debug("Failed to perform a force certification of resource {}. The status is {}. ", resource.getName(), status);
408 throw new ByResponseFormatComponentException(
409 componentUtils.getResponseFormatByResource(componentUtils.convertFromStorageResponse(status), resource));
411 result = ModelConverter.convertFromToscaElement(certifyResourceRes.left().value());
412 resource.setComponentMetadataDefinition(result.getComponentMetadataDefinition());
414 log.info("unlock component {}", resource.getUniqueId());
415 if (!inTransaction) {
416 if (result != null) {
417 janusGraphDao.commit();
419 janusGraphDao.rollback();
422 NodeTypeEnum nodeType = resource.getComponentType().getNodeType();
423 log.info("During change state, another component {} has been created/updated", resource.getUniqueId());
424 graphLockOperation.unlockComponent(resource.getUniqueId(), nodeType);
431 public boolean isFirstCertification(String previousVersion) {
432 return previousVersion.split("\\.")[0].equals("0");