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.openecomp.sdc.be.catalog.enums.ChangeTypeEnum;
30 import org.openecomp.sdc.be.components.impl.ComponentBusinessLogic;
31 import org.openecomp.sdc.be.components.impl.ProductBusinessLogic;
32 import org.openecomp.sdc.be.components.impl.ResourceBusinessLogic;
33 import org.openecomp.sdc.be.components.impl.ServiceBusinessLogic;
34 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
35 import org.openecomp.sdc.be.components.impl.exceptions.ByResponseFormatComponentException;
36 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
37 import org.openecomp.sdc.be.components.impl.version.VesionUpdateHandler;
38 import org.openecomp.sdc.be.components.lifecycle.LifecycleChangeInfoWithAction.LifecycleChanceActionEnum;
39 import org.openecomp.sdc.be.dao.api.ActionStatus;
40 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphDao;
41 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
42 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
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 @org.springframework.stereotype.Component("lifecycleBusinessLogic")
68 public class LifecycleBusinessLogic {
70 private static final String COMMENT = "comment";
71 private static final Logger log = Logger.getLogger(LifecycleBusinessLogic.class);
73 ToscaOperationFacade toscaOperationFacade;
75 NodeTemplateOperation nodeTemplateOperation;
77 CatalogOperation catalogOperations;
79 VesionUpdateHandler groupUpdateHandler;
81 private IGraphLockOperation graphLockOperation = null;
83 private JanusGraphDao janusGraphDao;
84 @javax.annotation.Resource
85 private ComponentsUtils componentUtils;
86 @javax.annotation.Resource
87 private ToscaElementLifecycleOperation lifecycleOperation;
90 private ServiceBusinessLogic serviceBusinessLogic;
93 private ResourceBusinessLogic resourceBusinessLogic;
96 private ProductBusinessLogic productBusinessLogic;
97 private Map<String, LifeCycleTransition> stateTransitions;
101 initStateOperations();
104 private void initStateOperations() {
105 stateTransitions = new HashMap<>();
106 LifeCycleTransition checkoutOp = new CheckoutTransition(componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao);
107 stateTransitions.put(checkoutOp.getName().name(), checkoutOp);
108 UndoCheckoutTransition undoCheckoutOp = new UndoCheckoutTransition(componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao);
109 undoCheckoutOp.setCatalogOperations(catalogOperations);
110 stateTransitions.put(undoCheckoutOp.getName().name(), undoCheckoutOp);
111 LifeCycleTransition checkinOp = new CheckinTransition(componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao,
113 stateTransitions.put(checkinOp.getName().name(), checkinOp);
114 CertificationChangeTransition successCertification = new CertificationChangeTransition(serviceBusinessLogic, LifeCycleTransitionEnum.CERTIFY,
115 componentUtils, lifecycleOperation, toscaOperationFacade, janusGraphDao);
116 successCertification.setNodeTemplateOperation(nodeTemplateOperation);
117 stateTransitions.put(successCertification.getName().name(), successCertification);
121 Map<String, LifeCycleTransition> getStartTransition() {
122 return stateTransitions;
125 // TODO: rhalili - should use changeComponentState when possible
126 public Either<Resource, ResponseFormat> changeState(String resourceId, User modifier, LifeCycleTransitionEnum transitionEnum,
127 LifecycleChangeInfoWithAction changeInfo, boolean inTransaction, boolean needLock) {
128 return changeComponentState(ComponentTypeEnum.RESOURCE, resourceId, modifier, transitionEnum, changeInfo, inTransaction, needLock);
131 public <T extends Component> Either<T, ResponseFormat> changeComponentState(ComponentTypeEnum componentType, String componentId, User modifier,
132 LifeCycleTransitionEnum transitionEnum,
133 LifecycleChangeInfoWithAction changeInfo, boolean inTransaction,
135 LifeCycleTransition lifeCycleTransition = stateTransitions.get(transitionEnum.name());
136 if (lifeCycleTransition == null) {
137 log.debug("state operation is not valid. operations allowed are: {}", LifeCycleTransitionEnum.valuesAsString());
138 ResponseFormat error = componentUtils.getInvalidContentErrorAndAudit(modifier, componentId, AuditingActionEnum.CHECKOUT_RESOURCE);
139 return Either.right(error);
141 log.debug("get resource from graph");
142 ResponseFormat errorResponse;
143 Either<T, ResponseFormat> eitherResourceResponse = getComponentForChange(componentType, componentId, modifier, lifeCycleTransition,
145 if (eitherResourceResponse.isRight()) {
146 return eitherResourceResponse;
148 T component = eitherResourceResponse.left().value();
149 String resourceCurrVersion = component.getVersion();
150 LifecycleStateEnum resourceCurrState = component.getLifecycleState();
152 if (!inTransaction && needLock) {
153 log.debug("lock component {}", componentId);
155 lockComponent(componentType, component);
156 } catch (ComponentException e) {
157 errorResponse = e.getResponseFormat();
158 componentUtils.auditComponent(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(),
159 new ResourceCommonInfo(componentType.getValue()),
160 ResourceVersionInfo.newBuilder().state(resourceCurrState.name()).version(resourceCurrVersion).build());
161 log.error("lock component {} failed", componentId);
162 return Either.right(errorResponse);
164 log.debug("after lock component {}", componentId);
167 Either<String, ResponseFormat> commentValidationResult = validateComment(changeInfo, transitionEnum);
168 if (commentValidationResult.isRight()) {
169 errorResponse = commentValidationResult.right().value();
170 componentUtils.auditComponent(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(),
171 new ResourceCommonInfo(componentType.getValue()),
172 ResourceVersionInfo.newBuilder().state(resourceCurrState.name()).version(resourceCurrVersion).build(),
173 changeInfo.getUserRemarks());
174 return Either.right(errorResponse);
176 changeInfo.setUserRemarks(commentValidationResult.left().value());
177 log.debug("after validate component");
178 Either<Boolean, ResponseFormat> validateHighestVersion = validateHighestVersion(modifier, lifeCycleTransition, component,
179 resourceCurrVersion, componentType);
180 if (validateHighestVersion.isRight()) {
181 return Either.right(validateHighestVersion.right().value());
183 log.debug("after validate Highest Version");
184 final T oldComponent = component;
185 Either<T, ResponseFormat> checkedInComponentEither = checkInBeforeCertifyIfNeeded(componentType, modifier, transitionEnum, changeInfo,
186 inTransaction, component);
187 if (checkedInComponentEither.isRight()) {
188 return Either.right(checkedInComponentEither.right().value());
190 component = checkedInComponentEither.left().value();
191 return changeState(component, lifeCycleTransition, componentType, modifier, changeInfo, inTransaction).left()
192 .bind(c -> updateCatalog(c, oldComponent, ChangeTypeEnum.LIFECYCLE));
194 component.setUniqueId(componentId);
195 if (!inTransaction && needLock) {
196 log.info("unlock component {}", componentId);
197 NodeTypeEnum nodeType = componentType.getNodeType();
198 log.info("During change state, another component {} has been created/updated", componentId);
199 graphLockOperation.unlockComponent(componentId, nodeType);
204 private <T extends Component> Either<T, ResponseFormat> updateCatalog(T component, T oldComponent, ChangeTypeEnum changeStatus) {
205 log.debug("updateCatalog start");
206 T result = component == null ? oldComponent : component;
207 if (component != null) {
208 ActionStatus status = catalogOperations.updateCatalog(changeStatus, component);
209 if (status != ActionStatus.OK) {
210 return Either.right(componentUtils.getResponseFormat(status));
213 return Either.left(result);
216 private <T extends Component> Either<T, ResponseFormat> checkInBeforeCertifyIfNeeded(ComponentTypeEnum componentType, User modifier,
217 LifeCycleTransitionEnum transitionEnum,
218 LifecycleChangeInfoWithAction changeInfo,
219 boolean inTransaction, T component) {
220 LifecycleStateEnum oldState = component.getLifecycleState();
221 log.debug("Certification request for resource {} ", component.getUniqueId());
222 if (oldState == LifecycleStateEnum.NOT_CERTIFIED_CHECKOUT && transitionEnum == LifeCycleTransitionEnum.CERTIFY) {
223 log.debug("Resource {} is in Checkout state perform checkin", component.getUniqueId());
224 Either<T, ResponseFormat> actionResponse = changeState(component, stateTransitions.get(LifeCycleTransitionEnum.CHECKIN.name()),
225 componentType, modifier, changeInfo, inTransaction);
226 if (actionResponse.isRight()) {
227 log.debug("Failed to check in Resource {} error {}", component.getUniqueId(), actionResponse.right().value());
229 return actionResponse;
231 return Either.left(component);
234 private <T extends Component> Either<T, ResponseFormat> changeState(T component, LifeCycleTransition lifeCycleTransition,
235 ComponentTypeEnum componentType, User modifier,
236 LifecycleChangeInfoWithAction changeInfo, boolean inTransaction) {
237 ResponseFormat errorResponse;
238 LifecycleStateEnum oldState = component.getLifecycleState();
239 String resourceCurrVersion = component.getVersion();
240 ComponentBusinessLogic bl = getComponentBL(componentType);
241 Either<User, ResponseFormat> ownerResult = lifeCycleTransition.getComponentOwner(component, componentType);
242 if (ownerResult.isRight()) {
243 return Either.right(ownerResult.right().value());
245 User owner = ownerResult.left().value();
246 log.info("owner of resource {} is {}", component.getUniqueId(), owner.getUserId());
247 Either<Boolean, ResponseFormat> stateValidationResult = lifeCycleTransition
248 .validateBeforeTransition(component, componentType, modifier, owner, oldState, changeInfo);
249 if (stateValidationResult.isRight()) {
250 log.error("Failed to validateBeforeTransition");
251 errorResponse = stateValidationResult.right().value();
252 componentUtils.auditComponent(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(),
253 new ResourceCommonInfo(componentType.getValue()),
254 ResourceVersionInfo.newBuilder().version(resourceCurrVersion).state(oldState.name()).build(), changeInfo.getUserRemarks());
255 return Either.right(errorResponse);
257 Either<T, ResponseFormat> operationResult = lifeCycleTransition
258 .changeState(componentType, component, bl, modifier, owner, false, inTransaction);
259 if (operationResult.isRight()) {
260 errorResponse = operationResult.right().value();
261 log.info("audit before sending error response");
262 componentUtils.auditComponentAdmin(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(), componentType,
263 ResourceVersionInfo.newBuilder().state(oldState.name()).version(resourceCurrVersion).build());
264 return Either.right(errorResponse);
266 Component resourceAfterOperation = operationResult.left().value() == null ? component : operationResult.left().value();
267 componentUtils.auditComponent(componentUtils.getResponseFormat(ActionStatus.OK), modifier, resourceAfterOperation,
268 lifeCycleTransition.getAuditingAction(), new ResourceCommonInfo(componentType.getValue()),
269 ResourceVersionInfo.newBuilder().state(oldState.name()).version(resourceCurrVersion).build(), changeInfo.getUserRemarks());
270 return operationResult;
273 private <T extends Component> Either<T, ResponseFormat> getComponentForChange(ComponentTypeEnum componentType, String componentId, User modifier,
274 LifeCycleTransition lifeCycleTransition,
275 LifecycleChangeInfoWithAction changeInfo) {
276 Either<T, StorageOperationStatus> eitherResourceResponse = toscaOperationFacade.getToscaElement(componentId);
277 ResponseFormat errorResponse;
278 if (eitherResourceResponse.isRight()) {
279 ActionStatus actionStatus = componentUtils.convertFromStorageResponse(eitherResourceResponse.right().value(), componentType);
280 errorResponse = componentUtils.getResponseFormat(actionStatus, Constants.EMPTY_STRING);
281 log.debug("audit before sending response");
282 componentUtils.auditComponent(errorResponse, modifier, lifeCycleTransition.getAuditingAction(),
283 new ResourceCommonInfo(componentId, componentType.getValue()), changeInfo.getUserRemarks());
284 return Either.right(errorResponse);
286 return Either.left(eitherResourceResponse.left().value());
289 private Either<Boolean, ResponseFormat> validateHighestVersion(User modifier, LifeCycleTransition lifeCycleTransition, Component component,
290 String resourceCurrVersion, ComponentTypeEnum componentType) {
291 ResponseFormat errorResponse;
292 if (!component.isHighestVersion()) {
293 log.debug("Component version {} is not the last version of component {}",
294 component.getComponentMetadataDefinition().getMetadataDataDefinition().getVersion(),
295 component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
296 errorResponse = componentUtils.getResponseFormat(ActionStatus.COMPONENT_HAS_NEWER_VERSION,
297 component.getComponentMetadataDefinition().getMetadataDataDefinition().getName(), componentType.getValue().toLowerCase());
298 componentUtils.auditComponentAdmin(errorResponse, modifier, component, lifeCycleTransition.getAuditingAction(), componentType,
299 ResourceVersionInfo.newBuilder().state(component.getLifecycleState().name()).version(resourceCurrVersion).build());
300 return Either.right(errorResponse);
302 return Either.left(true);
305 private Boolean lockComponent(ComponentTypeEnum componentType, Component component) {
306 NodeTypeEnum nodeType = componentType.getNodeType();
307 StorageOperationStatus lockResourceStatus = graphLockOperation.lockComponent(component.getUniqueId(), nodeType);
308 if (lockResourceStatus.equals(StorageOperationStatus.OK)) {
311 ActionStatus actionStatus = componentUtils.convertFromStorageResponse(lockResourceStatus);
312 throw new ByActionStatusComponentException(actionStatus,
313 component.getComponentMetadataDefinition().getMetadataDataDefinition().getName());
317 private Either<String, ResponseFormat> validateComment(LifecycleChangeInfoWithAction changeInfo, LifeCycleTransitionEnum transitionEnum) {
318 String comment = changeInfo.getUserRemarks();
319 if (LifeCycleTransitionEnum.CERTIFY == transitionEnum || LifeCycleTransitionEnum.CHECKIN == transitionEnum
322 if (!ValidationUtils.validateStringNotEmpty(comment)) {
323 log.debug("user comment cannot be empty or null.");
324 ResponseFormat errorResponse = componentUtils.getResponseFormat(ActionStatus.MISSING_DATA, COMMENT);
325 return Either.right(errorResponse);
327 comment = ValidationUtils.removeNoneUtf8Chars(comment);
328 comment = ValidationUtils.removeHtmlTags(comment);
329 comment = ValidationUtils.normaliseWhitespace(comment);
330 comment = ValidationUtils.stripOctets(comment);
331 if (!ValidationUtils.validateLength(comment, ValidationUtils.COMMENT_MAX_LENGTH)) {
332 log.debug("user comment exceeds limit.");
334 .right(componentUtils.getResponseFormat(ActionStatus.EXCEEDS_LIMIT, COMMENT, String.valueOf(ValidationUtils.COMMENT_MAX_LENGTH)));
336 if (!ValidationUtils.validateIsEnglish(comment)) {
337 return Either.right(componentUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
340 return Either.left(comment);
343 private ComponentBusinessLogic getComponentBL(ComponentTypeEnum componentTypeEnum) {
344 ComponentBusinessLogic businessLogic;
345 switch (componentTypeEnum) {
347 businessLogic = this.resourceBusinessLogic;
350 businessLogic = this.serviceBusinessLogic;
353 businessLogic = this.productBusinessLogic;
356 throw new IllegalArgumentException("Illegal component type:" + componentTypeEnum.getValue());
358 return businessLogic;
361 public Either<Component, ResponseFormat> getLatestComponentByUuid(ComponentTypeEnum componentTypeEnum, String uuid) {
362 Either<Component, StorageOperationStatus> latestVersionEither = toscaOperationFacade.getLatestComponentByUuid(uuid);
363 if (latestVersionEither.isRight()) {
364 return Either.right(componentUtils
365 .getResponseFormat(componentUtils.convertFromStorageResponse(latestVersionEither.right().value(), componentTypeEnum), uuid));
367 Component latestComponent = latestVersionEither.left().value();
368 return Either.left(latestComponent);
372 * Performs Force certification. Note that a Force certification is allowed for the first certification only, as only a state and a version is
373 * promoted due a Force certification, skipping other actions required if a previous certified version exists.
377 * @param lifecycleChangeInfo
378 * @param inTransaction
382 public Resource forceResourceCertification(Resource resource, User user, LifecycleChangeInfoWithAction lifecycleChangeInfo, boolean inTransaction,
384 Resource result = null;
385 Either<ToscaElement, StorageOperationStatus> certifyResourceRes = null;
386 if (lifecycleChangeInfo.getAction() != LifecycleChanceActionEnum.CREATE_FROM_CSAR) {
387 log.debug("Force certification is not allowed for the action {}. ", lifecycleChangeInfo.getAction());
388 throw new ByActionStatusComponentException(ActionStatus.NOT_ALLOWED);
390 if (!isFirstCertification(resource.getVersion())) {
391 log.debug("Failed to perform a force certification of resource{}. Force certification is allowed for the first certification only. ",
393 throw new ByActionStatusComponentException(ActionStatus.NOT_ALLOWED);
396 if (!inTransaction && needLock) {
397 log.info("lock component {}", resource.getUniqueId());
398 lockComponent(resource.getComponentType(), resource);
399 log.info("after lock component {}", resource.getUniqueId());
402 certifyResourceRes = lifecycleOperation
403 .forceCerificationOfToscaElement(resource.getUniqueId(), user.getUserId(), user.getUserId(), resource.getVersion());
404 if (certifyResourceRes.isRight()) {
405 StorageOperationStatus status = certifyResourceRes.right().value();
406 log.debug("Failed to perform a force certification of resource {}. The status is {}. ", resource.getName(), status);
407 throw new ByResponseFormatComponentException(
408 componentUtils.getResponseFormatByResource(componentUtils.convertFromStorageResponse(status), resource));
410 result = ModelConverter.convertFromToscaElement(certifyResourceRes.left().value());
411 resource.setComponentMetadataDefinition(result.getComponentMetadataDefinition());
413 log.info("unlock component {}", resource.getUniqueId());
414 if (!inTransaction) {
415 if (result != null) {
416 janusGraphDao.commit();
418 janusGraphDao.rollback();
421 NodeTypeEnum nodeType = resource.getComponentType().getNodeType();
422 log.info("During change state, another component {} has been created/updated", resource.getUniqueId());
423 graphLockOperation.unlockComponent(resource.getUniqueId(), nodeType);
430 public boolean isFirstCertification(String previousVersion) {
431 return previousVersion.split("\\.")[0].equals("0");