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.components.impl;
23 import com.google.gson.JsonElement;
24 import fj.data.Either;
25 import org.apache.commons.collections.CollectionUtils;
26 import org.apache.commons.collections.MapUtils;
27 import org.apache.commons.lang3.tuple.ImmutablePair;
28 import org.openecomp.sdc.be.config.BeEcompErrorManager;
29 import org.openecomp.sdc.be.dao.api.ActionStatus;
30 import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
31 import org.openecomp.sdc.be.datatypes.elements.*;
32 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
33 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
34 import org.openecomp.sdc.be.model.*;
35 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
36 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
37 import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
38 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
39 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
40 import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
41 import org.openecomp.sdc.be.resources.data.EntryData;
42 import org.openecomp.sdc.common.api.Constants;
43 import org.openecomp.sdc.common.log.wrappers.Logger;
44 import org.openecomp.sdc.exception.ResponseFormat;
45 import org.springframework.web.context.WebApplicationContext;
47 import javax.servlet.ServletContext;
49 import java.util.function.Supplier;
51 @org.springframework.stereotype.Component("propertyBusinessLogic")
52 public class PropertyBusinessLogic extends BaseBusinessLogic {
54 private static final String CREATE_PROPERTY = "CreateProperty";
56 private static final Logger log = Logger.getLogger(PropertyBusinessLogic.class);
58 private static final String EMPTY_VALUE = null;
60 protected static IElementOperation getElementDao(Class<IElementOperation> class1, ServletContext context) {
61 WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
63 WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
65 return webApplicationContext.getBean(class1);
68 public Either<Map<String, DataTypeDefinition>, ResponseFormat> getAllDataTypes() {
69 return getAllDataTypes(applicationDataTypeCache);
73 * Create new property on component in graph
77 * @param newPropertyDefinition
79 * @return either properties or response format
82 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> addPropertyToComponent(String componentId,
84 PropertyDefinition newPropertyDefinition,
86 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
88 validateUserExists(userId, "create Property", false);
90 Either<Component, StorageOperationStatus> serviceElement =
91 toscaOperationFacade.getToscaElement(componentId);
92 if (serviceElement.isRight()) {
93 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
96 Component component = serviceElement.left().value();
97 NodeTypeEnum nodeType = component.getComponentType().getNodeType();
98 StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType );
99 if (!lockResult.equals(StorageOperationStatus.OK)) {
100 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(), componentId);
101 log.info("Failed to lock component {}. Error - {}", componentId, lockResult);
102 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
107 if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
108 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
112 List<PropertyDefinition> properties = component.getProperties();
114 if(CollectionUtils.isEmpty(properties)) {
115 properties = new ArrayList<>();
118 if(isPropertyExistInComponent(properties, propertyName)) {
121 Either.right(componentsUtils.getResponseFormat(ActionStatus
122 .PROPERTY_ALREADY_EXIST, propertyName));
127 Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
128 if (allDataTypes.isRight()) {
129 result = Either.right(allDataTypes.right().value());
133 Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
135 // validate property default values
136 Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
137 if (defaultValuesValidation.isRight()) {
138 result = Either.right(defaultValuesValidation.right().value());
142 ToscaPropertyType type = getType(newPropertyDefinition.getType());
144 PropertyValueConverter converter = type.getConverter();
146 String innerType = null;
147 if (newPropertyDefinition != null) {
148 SchemaDefinition schema = newPropertyDefinition.getSchema();
149 if (schema != null) {
150 PropertyDataDefinition prop = schema.getProperty();
152 innerType = prop.getType();
155 String convertedValue = null;
156 if (newPropertyDefinition.getDefaultValue() != null) {
157 convertedValue = converter.convert(
158 newPropertyDefinition.getDefaultValue(), innerType, allDataTypes.left().value());
159 newPropertyDefinition.setDefaultValue(convertedValue);
163 Either<PropertyDefinition, StorageOperationStatus> addPropertyEither =
165 .addPropertyToComponent(propertyName, newPropertyDefinition, component);
167 if (addPropertyEither.isRight()) {
168 log.info("Failed to add new property {}. Error - {}", componentId,
169 addPropertyEither.right().value());
170 result = Either.right(componentsUtils.getResponseFormat(ActionStatus
176 result = Either.left(new EntryData<>(propertyName, newPropertyDefinition));
180 commitOrRollback(result);
182 graphLockOperation.unlockComponent(componentId, nodeType);
188 * Get property of component
193 * @return either properties or response format
196 public Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> getComponentProperty(String componentId, String propertyId, String userId) {
198 validateUserExists(userId, "create Component Instance", false);
199 // Get the resource from DB
200 Either<Component, StorageOperationStatus> status =
201 toscaOperationFacade.getToscaElement(componentId);
202 if (status.isRight()) {
203 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
205 Component component = status.left().value();
206 List<PropertyDefinition> properties = component.getProperties();
207 if(CollectionUtils.isEmpty(properties)) {
208 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
211 for(PropertyDefinition property : properties) {
212 if(property.getUniqueId().equals(propertyId)) {
213 return Either.left(new EntryData<>(property.getName(), property));
216 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
220 public Either<List<PropertyDefinition>, ResponseFormat> getPropertiesList(String componentId,
222 validateUserExists(userId, "create Component Instance", false);
224 // Get the resource from DB
225 ComponentParametersView filter = new ComponentParametersView(true);
226 filter.setIgnoreProperties(false);
227 Either<Component, StorageOperationStatus> status =
228 toscaOperationFacade.getToscaElement(componentId);
229 if (status.isRight()) {
230 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
232 Component component = status.left().value();
233 List<PropertyDefinition> properties = component.getProperties();
235 return Either.left(properties);
240 * delete property of component from graph
245 * @return either properties or response format
248 public Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> deletePropertyFromComponent(String componentId, String propertyId, String userId) {
250 Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> result = null;
252 validateUserExists(userId, "delete Property", false);
254 // Get the resource from DB
255 Either<Component, StorageOperationStatus> getComponentRes = toscaOperationFacade.getToscaElement(componentId);
256 if (getComponentRes.isRight()) {
257 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
260 Component component = getComponentRes.left().value();
261 NodeTypeEnum nodeType = component.getComponentType().getNodeType();
262 StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
263 if (!lockResult.equals(StorageOperationStatus.OK)) {
264 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(),
266 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
271 // verify that resource is checked-out and the user is the last
273 if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
274 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
278 // verify property exist in resource
279 Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty =
280 getComponentProperty(componentId, propertyId, userId);
281 if (statusGetProperty.isRight()) {
282 result = Either.right(statusGetProperty.right().value());
286 Map.Entry<String, PropertyDefinition> propertyDefinitionEntry = statusGetProperty.left().value();
288 // verify that the property is not used by operation
289 if (isPropertyUsedByOperation(component, propertyDefinitionEntry.getValue())) {
290 return Either.right(componentsUtils.getResponseFormat(ActionStatus
291 .PROPERTY_USED_BY_OPERATION));
294 StorageOperationStatus status =
295 toscaOperationFacade.deletePropertyOfComponent(component, propertyDefinitionEntry.getKey());
296 if (status != StorageOperationStatus.OK) {
297 result = Either.right(componentsUtils.getResponseFormat(componentsUtils
298 .convertFromStorageResponse(status), component.getName()));
301 result = Either.left(propertyDefinitionEntry);
305 commitOrRollback(result);
307 graphLockOperation.unlockComponent(componentId, nodeType);
311 public boolean isPropertyUsedByOperation(Component component,
312 PropertyDefinition propertyDefinitionEntry) {
314 // Component's own interfaces
315 Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
316 if(MapUtils.isNotEmpty(interfaces)){
317 for(Map.Entry<String, InterfaceDefinition> interfaceEntry : interfaces.entrySet()) {
318 if (isPropertyExistInOperationInterface(propertyDefinitionEntry, interfaceEntry.getValue())) {
324 // Component's child's component interfaces
325 if(isPropertyUsedInCIInterfaces(component.getComponentInstancesInterfaces(), propertyDefinitionEntry)){
329 // Component's parent's component interfaces
330 Either<List<Component>, StorageOperationStatus> componentList = toscaOperationFacade.getParentComponents(component.getUniqueId());
331 if(componentList.isLeft()){
332 for (Component parentComponent : componentList.left().value()) {
333 if(isPropertyUsedInCIInterfaces(parentComponent.getComponentInstancesInterfaces(), propertyDefinitionEntry)){
342 private boolean isPropertyUsedInCIInterfaces(Map<String, List<ComponentInstanceInterface>> componentInstanceInterfaces, PropertyDefinition propertyDefinitionEntry){
343 Optional<ComponentInstanceInterface> isPropertyExistInOperationInterface = Optional.empty();
344 if(MapUtils.isNotEmpty(componentInstanceInterfaces)){
345 isPropertyExistInOperationInterface = componentInstanceInterfaces.entrySet().stream()
346 .flatMap(interfaceEntry -> interfaceEntry.getValue().stream())
347 .filter(instanceInterface -> isPropertyExistInOperationInterface(propertyDefinitionEntry, instanceInterface))
350 return isPropertyExistInOperationInterface.isPresent();
353 private boolean isPropertyExistInOperationInterface(PropertyDefinition propertyDefinition,
354 InterfaceDefinition interfaceDefinition) {
355 Map<String, OperationDataDefinition> operations =
356 interfaceDefinition.getOperations();
357 for(Map.Entry<String, OperationDataDefinition> operationEntry : operations
359 Optional<OperationInputDefinition> inputWithDeletedPropertyCandidate =
360 getInputWithDeclaredProperty(propertyDefinition, operationEntry);
362 if(inputWithDeletedPropertyCandidate.isPresent()) {
369 private Optional<OperationInputDefinition> getInputWithDeclaredProperty(PropertyDefinition propertyDefinition,
370 Map.Entry<String, OperationDataDefinition> operationEntry) {
371 ListDataDefinition<OperationInputDefinition> inputs =
372 operationEntry.getValue().getInputs();
373 List<OperationInputDefinition> operationInputsList =
374 Objects.isNull(inputs) ? null : inputs.getListToscaDataDefinition();
376 if(CollectionUtils.isEmpty(operationInputsList)) {
377 return Optional.empty();
380 return operationInputsList.stream().filter(input -> input.getInputId().equals(propertyDefinition.getUniqueId())).findAny();
388 * @param newPropertyDefinition
390 * @return either properties or response format
393 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> updateComponentProperty(String componentId,
395 PropertyDefinition newPropertyDefinition,
398 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
400 Either<Component, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(
402 if (status.isRight()) {
403 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
405 Component component = status.left().value();
406 NodeTypeEnum nodeType = component.getComponentType().getNodeType();
408 if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
409 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
412 StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
413 if (!lockResult.equals(StorageOperationStatus.OK)) {
414 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(),
416 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
421 Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty =
422 getComponentProperty(componentId, propertyId, userId);
423 if (statusGetProperty.isRight()) {
424 result = Either.right(statusGetProperty.right().value());
427 String propertyName = statusGetProperty.left().value().getKey();
429 Either<PropertyDefinition, StorageOperationStatus> either =
430 toscaOperationFacade.updatePropertyOfComponent(component, newPropertyDefinition);
431 if (either.isRight()) {
432 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), component.getName()));
436 EntryData<String, PropertyDefinition> property = new EntryData<>(propertyName, either.left().value());
437 result = Either.left(property);
441 commitOrRollback(result);
442 graphLockOperation.unlockComponent(componentId, nodeType);
447 private boolean isPropertyExistInComponent(List<PropertyDefinition> properties, String propertyName) {
448 if(CollectionUtils.isEmpty(properties)) {
452 Optional<PropertyDefinition> propertyCandidate =
453 properties.stream().filter(property -> property.getName().equals(propertyName))
456 return propertyCandidate.isPresent();
459 private boolean isPropertyExist(List<PropertyDefinition> properties, String resourceUid, String propertyName, String propertyType) {
460 boolean result = false;
461 if (!CollectionUtils.isEmpty(properties)) {
462 for (PropertyDefinition propertyDefinition : properties) {
464 if ( propertyDefinition.getName().equals(propertyName) &&
465 (propertyDefinition.getParentUniqueId().equals(resourceUid) || !propertyDefinition.getType().equals(propertyType)) ) {
474 private Either<PropertyDefinition, StorageOperationStatus> handleProperty(PropertyDefinition newPropertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
476 StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(newPropertyDefinition, dataTypes);
477 if (validateAndUpdateProperty != StorageOperationStatus.OK) {
478 return Either.right(validateAndUpdateProperty);
481 return Either.left(newPropertyDefinition);
484 private StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
486 log.trace("Going to validate property type and value. {}", propertyDefinition);
488 String propertyType = propertyDefinition.getType();
489 String value = propertyDefinition.getDefaultValue();
491 ToscaPropertyType type = getType(propertyType);
494 DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
495 if (dataTypeDefinition == null) {
496 log.debug("The type {} of property cannot be found.", propertyType);
497 return StorageOperationStatus.INVALID_TYPE;
499 return validateAndUpdateComplexValue(propertyDefinition, propertyType, value, dataTypeDefinition, dataTypes);
503 Either<String, TitanOperationStatus> checkInnerType = getInnerType(type, propertyDefinition::getSchema);
504 if (checkInnerType.isRight()) {
505 return StorageOperationStatus.INVALID_TYPE;
507 innerType = checkInnerType.left().value();
509 log.trace("After validating property type {}", propertyType);
511 boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
512 if (!isValidProperty) {
513 log.info("The value {} of property from type {} is invalid", value, type);
514 return StorageOperationStatus.INVALID_VALUE;
517 PropertyValueConverter converter = type.getConverter();
519 if (isEmptyValue(value)) {
520 log.debug("Default value was not sent for property {}. Set default value to {}", propertyDefinition.getName(), EMPTY_VALUE);
521 propertyDefinition.setDefaultValue(EMPTY_VALUE);
522 } else if (!isEmptyValue(value)) {
523 String convertedValue = converter.convert(value, innerType, dataTypes);
524 propertyDefinition.setDefaultValue(convertedValue);
526 return StorageOperationStatus.OK;
529 private StorageOperationStatus validateAndUpdateComplexValue(IComplexDefaultValue propertyDefinition, String propertyType,
530 String value, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> dataTypes) {
532 ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
534 if (validateResult.right) {
535 log.debug("The value {} of property from type {} is invalid", propertyType, propertyType);
536 return StorageOperationStatus.INVALID_VALUE;
539 JsonElement jsonElement = validateResult.left;
541 log.trace("Going to update value in property definition {} {}" , propertyDefinition.getName() , jsonElement);
543 updateValue(propertyDefinition, jsonElement);
545 return StorageOperationStatus.OK;
548 private void updateValue(IComplexDefaultValue propertyDefinition, JsonElement jsonElement) {
550 propertyDefinition.setDefaultValue(getValueFromJsonElement(jsonElement));
555 protected String getValueFromJsonElement(JsonElement jsonElement) {
556 if (jsonElement == null || jsonElement.isJsonNull()) {
559 if(jsonElement.toString().isEmpty()){
562 return jsonElement.toString();
565 private Either<String, TitanOperationStatus> getInnerType(ToscaPropertyType type, Supplier<SchemaDefinition> schemeGen) {
566 String innerType = null;
567 if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
569 SchemaDefinition def = schemeGen.get();
571 log.debug("Schema doesn't exists for property of type {}", type);
572 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
574 PropertyDataDefinition propDef = def.getProperty();
575 if (propDef == null) {
576 log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
577 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
579 innerType = propDef.getType();
581 return Either.left(innerType);
585 protected boolean isValidValue(ToscaPropertyType type, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
586 if (isEmptyValue(value)) {
589 PropertyTypeValidator validator = type.getValidator();
590 return validator.isValid(value, innerType, dataTypes);
594 public boolean isEmptyValue(String value) {
595 return value == null;