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=========================================================
20 package org.openecomp.sdc.be.components.impl;
22 import com.google.gson.JsonElement;
23 import fj.data.Either;
24 import java.util.ArrayList;
25 import java.util.List;
27 import java.util.Objects;
28 import java.util.Optional;
29 import org.apache.commons.collections.CollectionUtils;
30 import org.apache.commons.collections.MapUtils;
31 import org.openecomp.sdc.be.components.impl.exceptions.BusinessLogicException;
32 import org.openecomp.sdc.be.config.BeEcompErrorManager;
33 import org.openecomp.sdc.be.dao.api.ActionStatus;
34 import org.openecomp.sdc.be.datatypes.elements.ListDataDefinition;
35 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
36 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
37 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
38 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
39 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
40 import org.openecomp.sdc.be.model.Component;
41 import org.openecomp.sdc.be.model.ComponentInstanceInterface;
42 import org.openecomp.sdc.be.model.ComponentParametersView;
43 import org.openecomp.sdc.be.model.DataTypeDefinition;
44 import org.openecomp.sdc.be.model.InterfaceDefinition;
45 import org.openecomp.sdc.be.model.PropertyDefinition;
46 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ArtifactsOperations;
47 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.InterfaceOperation;
48 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.ToscaOperationException;
49 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
50 import org.openecomp.sdc.be.model.operations.api.IGroupInstanceOperation;
51 import org.openecomp.sdc.be.model.operations.api.IGroupOperation;
52 import org.openecomp.sdc.be.model.operations.api.IGroupTypeOperation;
53 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
54 import org.openecomp.sdc.be.model.operations.impl.InterfaceLifecycleOperation;
55 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
56 import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
57 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
58 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
59 import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
60 import org.openecomp.sdc.be.resources.data.EntryData;
61 import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
62 import org.openecomp.sdc.common.log.wrappers.Logger;
63 import org.openecomp.sdc.exception.ResponseFormat;
64 import org.springframework.beans.factory.annotation.Autowired;
66 @org.springframework.stereotype.Component("propertyBusinessLogic")
67 public class PropertyBusinessLogic extends BaseBusinessLogic {
69 private static final String CREATE_PROPERTY = "CreateProperty";
70 private static final Logger log = Logger.getLogger(PropertyBusinessLogic.class);
71 private static final String EMPTY_VALUE = null;
74 public PropertyBusinessLogic(IElementOperation elementDao, IGroupOperation groupOperation, IGroupInstanceOperation groupInstanceOperation,
75 IGroupTypeOperation groupTypeOperation, InterfaceOperation interfaceOperation,
76 InterfaceLifecycleOperation interfaceLifecycleTypeOperation, ArtifactsOperations artifactToscaOperation) {
77 super(elementDao, groupOperation, groupInstanceOperation, groupTypeOperation, interfaceOperation, interfaceLifecycleTypeOperation,
78 artifactToscaOperation);
82 * Create new property on component in graph
85 * @param newPropertyDefinition
87 * @return either properties or response format
89 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> addPropertyToComponent(String componentId,
90 PropertyDefinition newPropertyDefinition,
92 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
93 validateUserExists(userId);
94 Either<Component, StorageOperationStatus> serviceElement = toscaOperationFacade.getToscaElement(componentId);
95 if (serviceElement.isRight()) {
96 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
99 Component component = serviceElement.left().value();
100 NodeTypeEnum nodeType = component.getComponentType().getNodeType();
101 StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
102 if (!lockResult.equals(StorageOperationStatus.OK)) {
103 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(), componentId);
104 log.info("Failed to lock component {}. Error - {}", componentId, lockResult);
105 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
109 final String propertyName = newPropertyDefinition.getName();
110 if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
111 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
114 List<PropertyDefinition> properties = component.getProperties();
115 if (CollectionUtils.isEmpty(properties)) {
116 properties = new ArrayList<>();
118 if (isPropertyExistInComponent(properties, propertyName)) {
119 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_ALREADY_EXIST, propertyName));
122 Map<String, DataTypeDefinition> allDataTypes = componentsUtils.getAllDataTypes(applicationDataTypeCache, component.getModel());
123 // validate property default values
124 Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, allDataTypes);
125 if (defaultValuesValidation.isRight()) {
126 result = Either.right(defaultValuesValidation.right().value());
130 ToscaPropertyType type = getType(newPropertyDefinition.getType());
132 PropertyValueConverter converter = type.getConverter();
134 String innerType = null;
135 SchemaDefinition schema = newPropertyDefinition.getSchema();
136 if (schema != null) {
137 PropertyDataDefinition prop = schema.getProperty();
139 innerType = prop.getType();
142 if (newPropertyDefinition.getDefaultValue() != null) {
143 final String convertedValue = converter.convert(newPropertyDefinition.getDefaultValue(), innerType, allDataTypes);
144 newPropertyDefinition.setDefaultValue(convertedValue);
147 Either<PropertyDefinition, StorageOperationStatus> addPropertyEither = toscaOperationFacade
148 .addPropertyToComponent(newPropertyDefinition, component);
149 if (addPropertyEither.isRight()) {
150 log.info("Failed to add new property {}. Error - {}", componentId, addPropertyEither.right().value());
151 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
155 result = Either.left(new EntryData<>(propertyName, newPropertyDefinition));
158 commitOrRollback(result);
160 graphLockOperation.unlockComponent(componentId, nodeType);
165 * Copies a list of properties to a component.
167 * @param component the component to add the copied properties
168 * @param propertiesToCopyList the properties to be copied
169 * @return the updated component with the copied properties.
170 * @throws ToscaOperationException when a problem happens during the copy operation
172 public Component copyPropertyToComponent(final Component component, final List<PropertyDefinition> propertiesToCopyList)
173 throws ToscaOperationException {
174 return copyPropertyToComponent(component, propertiesToCopyList, true);
178 * Copies a list of properties to a component.
180 * @param component the component to add the copied properties
181 * @param propertiesToCopyList the properties to be copied
182 * @param refreshComponent refresh the component from database after update
183 * @return the component refreshed from database if refreshComponent is {@code true}, the same component reference otherwise
184 * @throws ToscaOperationException when a problem happens during the copy operation
186 public Component copyPropertyToComponent(final Component component, final List<PropertyDefinition> propertiesToCopyList,
187 final boolean refreshComponent) throws ToscaOperationException {
188 if (CollectionUtils.isEmpty(propertiesToCopyList)) {
191 for (final PropertyDefinition propertyDefinition : propertiesToCopyList) {
192 copyPropertyToComponent(component, propertyDefinition);
194 if (refreshComponent) {
195 return toscaOperationFacade.getToscaElement(component.getUniqueId()).left().value();
201 * Copies one property to a component.
203 * @param component the component to add the copied property
204 * @param propertyDefinition the property to be copied
205 * @throws ToscaOperationException when a problem happens during the copy operation
207 private void copyPropertyToComponent(final Component component, final PropertyDefinition propertyDefinition) throws ToscaOperationException {
208 final PropertyDefinition copiedPropertyDefinition = new PropertyDefinition(propertyDefinition);
209 final String componentId = component.getUniqueId();
210 final String propertyName = copiedPropertyDefinition.getName();
211 copiedPropertyDefinition.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(componentId, propertyName));
212 copiedPropertyDefinition.setParentUniqueId(componentId);
213 final Either<PropertyDefinition, StorageOperationStatus> operationResult = toscaOperationFacade
214 .addPropertyToComponent(copiedPropertyDefinition, component);
215 if (operationResult.isRight()) {
216 final String error = String
217 .format("Failed to add copied property '%s' to component '%s'. Operation status: '%s'", propertyDefinition.getUniqueId(), componentId,
218 operationResult.right().value());
219 log.error(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR, PropertyBusinessLogic.class.getName(), "catalog-be", error);
220 throw new ToscaOperationException(error, operationResult.right().value());
225 * Get property of component
230 * @return either properties or response format
232 public Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> getComponentProperty(String componentId, String propertyId, String userId) {
233 validateUserExists(userId);
234 // Get the resource from DB
235 Either<Component, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(componentId);
236 if (status.isRight()) {
237 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
239 Component component = status.left().value();
240 List<PropertyDefinition> properties = component.getProperties();
241 if (CollectionUtils.isEmpty(properties)) {
242 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
244 for (PropertyDefinition property : properties) {
245 if (property.getUniqueId().equals(propertyId)) {
246 return Either.left(new EntryData<>(property.getName(), property));
249 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
252 public Either<List<PropertyDefinition>, ResponseFormat> getPropertiesList(String componentId, String userId) {
253 validateUserExists(userId);
254 // Get the resource from DB
255 ComponentParametersView filter = new ComponentParametersView(true);
256 filter.setIgnoreProperties(false);
257 Either<Component, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(componentId);
258 if (status.isRight()) {
259 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
261 Component component = status.left().value();
262 List<PropertyDefinition> properties = component.getProperties();
263 return Either.left(properties);
267 * delete property of component from graph
272 * @return either properties or response format
274 public Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> deletePropertyFromComponent(String componentId, String propertyId,
276 Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> result = null;
277 validateUserExists(userId);
278 // Get the resource from DB
279 Either<Component, StorageOperationStatus> getComponentRes = toscaOperationFacade.getToscaElement(componentId);
280 if (getComponentRes.isRight()) {
281 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
284 Component component = getComponentRes.left().value();
285 NodeTypeEnum nodeType = component.getComponentType().getNodeType();
286 StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
287 if (!lockResult.equals(StorageOperationStatus.OK)) {
288 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(), componentId);
289 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
293 // verify that resource is checked-out and the user is the last
296 if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
297 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
300 // verify property exist in resource
301 Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty = getComponentProperty(componentId, propertyId, userId);
302 if (statusGetProperty.isRight()) {
303 result = Either.right(statusGetProperty.right().value());
306 Map.Entry<String, PropertyDefinition> propertyDefinitionEntry = statusGetProperty.left().value();
307 // verify that the property is not used by operation
308 if (isPropertyUsedByOperation(component, propertyDefinitionEntry.getValue())) {
309 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_USED_BY_OPERATION));
311 StorageOperationStatus status = toscaOperationFacade.deletePropertyOfComponent(component, propertyDefinitionEntry.getKey());
312 if (status != StorageOperationStatus.OK) {
313 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status), component.getName()));
316 result = Either.left(propertyDefinitionEntry);
319 commitOrRollback(result);
321 graphLockOperation.unlockComponent(componentId, nodeType);
325 public boolean isPropertyUsedByOperation(Component component, PropertyDefinition propertyDefinitionEntry) {
326 // Component's own interfaces
327 Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
328 if (MapUtils.isNotEmpty(interfaces)) {
329 for (Map.Entry<String, InterfaceDefinition> interfaceEntry : interfaces.entrySet()) {
330 if (isPropertyExistInOperationInterface(propertyDefinitionEntry, interfaceEntry.getValue())) {
335 // Component's child's component interfaces
336 if (isPropertyUsedInCIInterfaces(component.getComponentInstancesInterfaces(), propertyDefinitionEntry)) {
339 // Component's parent's component interfaces
340 Either<List<Component>, StorageOperationStatus> componentList = toscaOperationFacade.getParentComponents(component.getUniqueId());
341 if (componentList.isLeft()) {
342 for (Component parentComponent : componentList.left().value()) {
343 if (isPropertyUsedInCIInterfaces(parentComponent.getComponentInstancesInterfaces(), propertyDefinitionEntry)) {
351 private boolean isPropertyUsedInCIInterfaces(Map<String, List<ComponentInstanceInterface>> componentInstanceInterfaces,
352 PropertyDefinition propertyDefinitionEntry) {
353 Optional<ComponentInstanceInterface> isPropertyExistInOperationInterface = Optional.empty();
354 if (MapUtils.isNotEmpty(componentInstanceInterfaces)) {
355 isPropertyExistInOperationInterface = componentInstanceInterfaces.entrySet().stream()
356 .flatMap(interfaceEntry -> interfaceEntry.getValue().stream())
357 .filter(instanceInterface -> isPropertyExistInOperationInterface(propertyDefinitionEntry, instanceInterface)).findAny();
359 return isPropertyExistInOperationInterface.isPresent();
362 private boolean isPropertyExistInOperationInterface(PropertyDefinition propertyDefinition, InterfaceDefinition interfaceDefinition) {
363 Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
364 for (Map.Entry<String, OperationDataDefinition> operationEntry : operations.entrySet()) {
365 Optional<OperationInputDefinition> inputWithDeletedPropertyCandidate = getInputWithDeclaredProperty(propertyDefinition, operationEntry);
366 if (inputWithDeletedPropertyCandidate.isPresent()) {
373 private Optional<OperationInputDefinition> getInputWithDeclaredProperty(PropertyDefinition propertyDefinition,
374 Map.Entry<String, OperationDataDefinition> operationEntry) {
375 ListDataDefinition<OperationInputDefinition> inputs = operationEntry.getValue().getInputs();
376 List<OperationInputDefinition> operationInputsList = Objects.isNull(inputs) ? null : inputs.getListToscaDataDefinition();
377 if (CollectionUtils.isEmpty(operationInputsList)) {
378 return Optional.empty();
380 return operationInputsList.stream().filter(
381 input -> input.getInputId().equals(propertyDefinition.getUniqueId()) || (input.getSourceProperty() != null && input.getSourceProperty()
382 .equals(propertyDefinition.getUniqueId()))).findAny();
390 * @param newPropertyDefinition
392 * @return either properties or response format
394 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> updateComponentProperty(String componentId, String propertyId,
395 PropertyDefinition newPropertyDefinition,
397 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
398 Either<Component, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(componentId);
399 if (status.isRight()) {
400 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
402 Component component = status.left().value();
403 NodeTypeEnum nodeType = component.getComponentType().getNodeType();
404 if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
405 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
407 StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
408 if (!lockResult.equals(StorageOperationStatus.OK)) {
409 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(), componentId);
410 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
414 Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty = getComponentProperty(componentId, propertyId, userId);
415 if (statusGetProperty.isRight()) {
416 result = Either.right(statusGetProperty.right().value());
419 String propertyName = statusGetProperty.left().value().getKey();
420 Either<PropertyDefinition, StorageOperationStatus> either = toscaOperationFacade
421 .updatePropertyOfComponent(component, newPropertyDefinition);
422 if (either.isRight()) {
423 result = Either.right(componentsUtils
424 .getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), component.getName()));
427 EntryData<String, PropertyDefinition> property = new EntryData<>(propertyName, either.left().value());
428 result = Either.left(property);
431 commitOrRollback(result);
432 graphLockOperation.unlockComponent(componentId, nodeType);
437 * Finds a component by id,
439 * @param componentId the component id to find
440 * @return an Optional<Component> if the component with given id was found, otherwise Optional.empty()
441 * @throws BusinessLogicException when a problem happens during the find operation
443 public Optional<Component> findComponentById(final String componentId) throws BusinessLogicException {
444 final Either<Component, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(componentId);
445 if (status.isRight()) {
446 final StorageOperationStatus operationStatus = status.right().value();
447 if (operationStatus == StorageOperationStatus.NOT_FOUND) {
448 return Optional.empty();
450 final ResponseFormat responseFormat = componentsUtils.getResponseFormat(operationStatus);
451 throw new BusinessLogicException(responseFormat);
453 return Optional.ofNullable(status.left().value());
457 * Updates a component property.
459 * @param componentId the component id that owns the property
460 * @param propertyDefinition the existing property to update
461 * @return the updated property
462 * @throws BusinessLogicException if the component was not found or if there was a problem during the update operation.
464 public PropertyDefinition updateComponentProperty(final String componentId, final PropertyDefinition propertyDefinition)
465 throws BusinessLogicException {
466 final Component component = findComponentById(componentId).orElse(null);
467 if (component == null) {
468 throw new BusinessLogicException(componentsUtils.getResponseFormatByResource(ActionStatus.RESOURCE_NOT_FOUND, componentId));
470 final Either<PropertyDefinition, StorageOperationStatus> updateResultEither = toscaOperationFacade
471 .updatePropertyOfComponent(component, propertyDefinition);
472 if (updateResultEither.isRight()) {
473 final ResponseFormat responseFormat = componentsUtils
474 .getResponseFormatByResource(componentsUtils.convertFromStorageResponse(updateResultEither.right().value()), component.getName());
475 throw new BusinessLogicException(responseFormat);
477 return updateResultEither.left().value();
480 private boolean isPropertyExistInComponent(List<PropertyDefinition> properties, String propertyName) {
481 if (CollectionUtils.isEmpty(properties)) {
484 Optional<PropertyDefinition> propertyCandidate = properties.stream().filter(property -> property.getName().equals(propertyName)).findAny();
485 return propertyCandidate.isPresent();
489 protected String getValueFromJsonElement(JsonElement jsonElement) {
490 if (jsonElement == null || jsonElement.isJsonNull()) {
493 if (jsonElement.toString().isEmpty()) {
496 return jsonElement.toString();
500 protected boolean isValidValue(ToscaPropertyType type, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
501 if (isEmptyValue(value)) {
504 PropertyTypeValidator validator = type.getValidator();
505 return validator.isValid(value, innerType, dataTypes);
509 public boolean isEmptyValue(String value) {
510 return value == null;