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.ListDataDefinition;
32 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
33 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
34 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
35 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
36 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
37 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
38 import org.openecomp.sdc.be.model.Component;
39 import org.openecomp.sdc.be.model.ComponentInstanceInterface;
40 import org.openecomp.sdc.be.model.ComponentParametersView;
41 import org.openecomp.sdc.be.model.DataTypeDefinition;
42 import org.openecomp.sdc.be.model.IComplexDefaultValue;
43 import org.openecomp.sdc.be.model.InterfaceDefinition;
44 import org.openecomp.sdc.be.model.PropertyDefinition;
45 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
46 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
47 import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
48 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
49 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
50 import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
51 import org.openecomp.sdc.be.resources.data.EntryData;
52 import org.openecomp.sdc.common.api.Constants;
53 import org.openecomp.sdc.common.log.wrappers.Logger;
54 import org.openecomp.sdc.exception.ResponseFormat;
55 import org.springframework.web.context.WebApplicationContext;
57 import javax.servlet.ServletContext;
59 import java.util.function.Supplier;
61 @org.springframework.stereotype.Component("propertyBusinessLogic")
62 public class PropertyBusinessLogic extends BaseBusinessLogic {
64 private static final String CREATE_PROPERTY = "CreateProperty";
66 private static final Logger log = Logger.getLogger(PropertyBusinessLogic.class);
68 private static final String EMPTY_VALUE = null;
70 protected static IElementOperation getElementDao(Class<IElementOperation> class1, ServletContext context) {
71 WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
73 WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
75 return webApplicationContext.getBean(class1);
78 public Either<Map<String, DataTypeDefinition>, ResponseFormat> getAllDataTypes() {
79 return getAllDataTypes(applicationDataTypeCache);
83 * Create new property on component in graph
87 * @param newPropertyDefinition
89 * @return either properties or response format
92 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> addPropertyToComponent(String componentId,
94 PropertyDefinition newPropertyDefinition,
96 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
98 validateUserExists(userId, "create Property", false);
100 Either<Component, StorageOperationStatus> serviceElement =
101 toscaOperationFacade.getToscaElement(componentId);
102 if (serviceElement.isRight()) {
103 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
106 Component component = serviceElement.left().value();
107 NodeTypeEnum nodeType = component.getComponentType().getNodeType();
108 StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType );
109 if (!lockResult.equals(StorageOperationStatus.OK)) {
110 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(), componentId);
111 log.info("Failed to lock component {}. Error - {}", componentId, lockResult);
112 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
117 if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
118 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
122 List<PropertyDefinition> properties = component.getProperties();
124 if(CollectionUtils.isEmpty(properties)) {
125 properties = new ArrayList<>();
128 if(isPropertyExistInComponent(properties, propertyName)) {
131 Either.right(componentsUtils.getResponseFormat(ActionStatus
132 .PROPERTY_ALREADY_EXIST, propertyName));
137 Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
138 if (allDataTypes.isRight()) {
139 result = Either.right(allDataTypes.right().value());
143 Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
145 // validate property default values
146 Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
147 if (defaultValuesValidation.isRight()) {
148 result = Either.right(defaultValuesValidation.right().value());
152 ToscaPropertyType type = getType(newPropertyDefinition.getType());
154 PropertyValueConverter converter = type.getConverter();
156 String innerType = null;
157 if (newPropertyDefinition != null) {
158 SchemaDefinition schema = newPropertyDefinition.getSchema();
159 if (schema != null) {
160 PropertyDataDefinition prop = schema.getProperty();
162 innerType = prop.getType();
165 String convertedValue = null;
166 if (newPropertyDefinition.getDefaultValue() != null) {
167 convertedValue = converter.convert(
168 newPropertyDefinition.getDefaultValue(), innerType, allDataTypes.left().value());
169 newPropertyDefinition.setDefaultValue(convertedValue);
173 Either<PropertyDefinition, StorageOperationStatus> addPropertyEither =
175 .addPropertyToComponent(propertyName, newPropertyDefinition, component);
177 if (addPropertyEither.isRight()) {
178 log.info("Failed to add new property {}. Error - {}", componentId,
179 addPropertyEither.right().value());
180 result = Either.right(componentsUtils.getResponseFormat(ActionStatus
186 result = Either.left(new EntryData<>(propertyName, newPropertyDefinition));
190 commitOrRollback(result);
192 graphLockOperation.unlockComponent(componentId, nodeType);
198 * Get property of component
203 * @return either properties or response format
206 public Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> getComponentProperty(String componentId, String propertyId, String userId) {
208 validateUserExists(userId, "create Component Instance", false);
209 // Get the resource from DB
210 Either<Component, StorageOperationStatus> status =
211 toscaOperationFacade.getToscaElement(componentId);
212 if (status.isRight()) {
213 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
215 Component component = status.left().value();
216 List<PropertyDefinition> properties = component.getProperties();
217 if(CollectionUtils.isEmpty(properties)) {
218 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
221 for(PropertyDefinition property : properties) {
222 if(property.getUniqueId().equals(propertyId)) {
223 return Either.left(new EntryData<>(property.getName(), property));
226 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
230 public Either<List<PropertyDefinition>, ResponseFormat> getPropertiesList(String componentId,
232 validateUserExists(userId, "create Component Instance", false);
234 // Get the resource from DB
235 ComponentParametersView filter = new ComponentParametersView(true);
236 filter.setIgnoreProperties(false);
237 Either<Component, StorageOperationStatus> status =
238 toscaOperationFacade.getToscaElement(componentId);
239 if (status.isRight()) {
240 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
242 Component component = status.left().value();
243 List<PropertyDefinition> properties = component.getProperties();
245 return Either.left(properties);
250 * delete property of component from graph
255 * @return either properties or response format
258 public Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> deletePropertyFromComponent(String componentId, String propertyId, String userId) {
260 Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> result = null;
262 validateUserExists(userId, "delete Property", false);
264 // Get the resource from DB
265 Either<Component, StorageOperationStatus> getComponentRes = toscaOperationFacade.getToscaElement(componentId);
266 if (getComponentRes.isRight()) {
267 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
270 Component component = getComponentRes.left().value();
271 NodeTypeEnum nodeType = component.getComponentType().getNodeType();
272 StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
273 if (!lockResult.equals(StorageOperationStatus.OK)) {
274 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(),
276 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
281 // verify that resource is checked-out and the user is the last
283 if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
284 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
288 // verify property exist in resource
289 Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty =
290 getComponentProperty(componentId, propertyId, userId);
291 if (statusGetProperty.isRight()) {
292 result = Either.right(statusGetProperty.right().value());
296 Map.Entry<String, PropertyDefinition> propertyDefinitionEntry = statusGetProperty.left().value();
298 // verify that the property is not used by operation
299 if (isPropertyUsedByOperation(component, propertyDefinitionEntry.getValue())) {
300 return Either.right(componentsUtils.getResponseFormat(ActionStatus
301 .PROPERTY_USED_BY_OPERATION));
304 StorageOperationStatus status =
305 toscaOperationFacade.deletePropertyOfComponent(component, propertyDefinitionEntry.getKey());
306 if (status != StorageOperationStatus.OK) {
307 result = Either.right(componentsUtils.getResponseFormat(componentsUtils
308 .convertFromStorageResponse(status), component.getName()));
311 result = Either.left(propertyDefinitionEntry);
315 commitOrRollback(result);
317 graphLockOperation.unlockComponent(componentId, nodeType);
321 public boolean isPropertyUsedByOperation(Component component,
322 PropertyDefinition propertyDefinitionEntry) {
324 // Component's own interfaces
325 Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
326 if(MapUtils.isNotEmpty(interfaces)){
327 for(Map.Entry<String, InterfaceDefinition> interfaceEntry : interfaces.entrySet()) {
328 if (isPropertyExistInOperationInterface(propertyDefinitionEntry, interfaceEntry.getValue())) {
334 // Component's child's component interfaces
335 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)){
352 private boolean isPropertyUsedInCIInterfaces(Map<String, List<ComponentInstanceInterface>> componentInstanceInterfaces, 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))
360 return isPropertyExistInOperationInterface.isPresent();
363 private boolean isPropertyExistInOperationInterface(PropertyDefinition propertyDefinition,
364 InterfaceDefinition interfaceDefinition) {
365 Map<String, OperationDataDefinition> operations =
366 interfaceDefinition.getOperations();
367 for(Map.Entry<String, OperationDataDefinition> operationEntry : operations
369 Optional<OperationInputDefinition> inputWithDeletedPropertyCandidate =
370 getInputWithDeclaredProperty(propertyDefinition, operationEntry);
372 if(inputWithDeletedPropertyCandidate.isPresent()) {
379 private Optional<OperationInputDefinition> getInputWithDeclaredProperty(PropertyDefinition propertyDefinition,
380 Map.Entry<String, OperationDataDefinition> operationEntry) {
381 ListDataDefinition<OperationInputDefinition> inputs =
382 operationEntry.getValue().getInputs();
383 List<OperationInputDefinition> operationInputsList =
384 Objects.isNull(inputs) ? null : inputs.getListToscaDataDefinition();
386 if(CollectionUtils.isEmpty(operationInputsList)) {
387 return Optional.empty();
390 return operationInputsList.stream().filter(input -> input.getInputId().equals(propertyDefinition.getUniqueId())
391 || (input.getSourceProperty() != null && input.getSourceProperty().equals(propertyDefinition.getUniqueId()))).findAny();
399 * @param newPropertyDefinition
401 * @return either properties or response format
404 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> updateComponentProperty(String componentId,
406 PropertyDefinition newPropertyDefinition,
409 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
411 Either<Component, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(
413 if (status.isRight()) {
414 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
416 Component component = status.left().value();
417 NodeTypeEnum nodeType = component.getComponentType().getNodeType();
419 if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
420 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
423 StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
424 if (!lockResult.equals(StorageOperationStatus.OK)) {
425 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(),
427 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
432 Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty =
433 getComponentProperty(componentId, propertyId, userId);
434 if (statusGetProperty.isRight()) {
435 result = Either.right(statusGetProperty.right().value());
438 String propertyName = statusGetProperty.left().value().getKey();
440 Either<PropertyDefinition, StorageOperationStatus> either =
441 toscaOperationFacade.updatePropertyOfComponent(component, newPropertyDefinition);
442 if (either.isRight()) {
443 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), component.getName()));
447 EntryData<String, PropertyDefinition> property = new EntryData<>(propertyName, either.left().value());
448 result = Either.left(property);
452 commitOrRollback(result);
453 graphLockOperation.unlockComponent(componentId, nodeType);
458 private boolean isPropertyExistInComponent(List<PropertyDefinition> properties, String propertyName) {
459 if(CollectionUtils.isEmpty(properties)) {
463 Optional<PropertyDefinition> propertyCandidate =
464 properties.stream().filter(property -> property.getName().equals(propertyName))
467 return propertyCandidate.isPresent();
470 private boolean isPropertyExist(List<PropertyDefinition> properties, String resourceUid, String propertyName, String propertyType) {
471 boolean result = false;
472 if (!CollectionUtils.isEmpty(properties)) {
473 for (PropertyDefinition propertyDefinition : properties) {
475 if ( propertyDefinition.getName().equals(propertyName) &&
476 (propertyDefinition.getParentUniqueId().equals(resourceUid) || !propertyDefinition.getType().equals(propertyType)) ) {
485 private Either<PropertyDefinition, StorageOperationStatus> handleProperty(PropertyDefinition newPropertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
487 StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(newPropertyDefinition, dataTypes);
488 if (validateAndUpdateProperty != StorageOperationStatus.OK) {
489 return Either.right(validateAndUpdateProperty);
492 return Either.left(newPropertyDefinition);
495 private StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
497 log.trace("Going to validate property type and value. {}", propertyDefinition);
499 String propertyType = propertyDefinition.getType();
500 String value = propertyDefinition.getDefaultValue();
502 ToscaPropertyType type = getType(propertyType);
505 DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
506 if (dataTypeDefinition == null) {
507 log.debug("The type {} of property cannot be found.", propertyType);
508 return StorageOperationStatus.INVALID_TYPE;
510 return validateAndUpdateComplexValue(propertyDefinition, propertyType, value, dataTypeDefinition, dataTypes);
514 Either<String, TitanOperationStatus> checkInnerType = getInnerType(type, propertyDefinition::getSchema);
515 if (checkInnerType.isRight()) {
516 return StorageOperationStatus.INVALID_TYPE;
518 innerType = checkInnerType.left().value();
520 log.trace("After validating property type {}", propertyType);
522 boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
523 if (!isValidProperty) {
524 log.info("The value {} of property from type {} is invalid", value, type);
525 return StorageOperationStatus.INVALID_VALUE;
528 PropertyValueConverter converter = type.getConverter();
530 if (isEmptyValue(value)) {
531 log.debug("Default value was not sent for property {}. Set default value to {}", propertyDefinition.getName(), EMPTY_VALUE);
532 propertyDefinition.setDefaultValue(EMPTY_VALUE);
533 } else if (!isEmptyValue(value)) {
534 String convertedValue = converter.convert(value, innerType, dataTypes);
535 propertyDefinition.setDefaultValue(convertedValue);
537 return StorageOperationStatus.OK;
540 private StorageOperationStatus validateAndUpdateComplexValue(IComplexDefaultValue propertyDefinition, String propertyType,
541 String value, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> dataTypes) {
543 ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
545 if (validateResult.right) {
546 log.debug("The value {} of property from type {} is invalid", propertyType, propertyType);
547 return StorageOperationStatus.INVALID_VALUE;
550 JsonElement jsonElement = validateResult.left;
552 log.trace("Going to update value in property definition {} {}" , propertyDefinition.getName() , jsonElement);
554 updateValue(propertyDefinition, jsonElement);
556 return StorageOperationStatus.OK;
559 private void updateValue(IComplexDefaultValue propertyDefinition, JsonElement jsonElement) {
561 propertyDefinition.setDefaultValue(getValueFromJsonElement(jsonElement));
566 protected String getValueFromJsonElement(JsonElement jsonElement) {
567 if (jsonElement == null || jsonElement.isJsonNull()) {
570 if(jsonElement.toString().isEmpty()){
573 return jsonElement.toString();
576 private Either<String, TitanOperationStatus> getInnerType(ToscaPropertyType type, Supplier<SchemaDefinition> schemeGen) {
577 String innerType = null;
578 if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
580 SchemaDefinition def = schemeGen.get();
582 log.debug("Schema doesn't exists for property of type {}", type);
583 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
585 PropertyDataDefinition propDef = def.getProperty();
586 if (propDef == null) {
587 log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
588 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
590 innerType = propDef.getType();
592 return Either.left(innerType);
596 protected boolean isValidValue(ToscaPropertyType type, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
597 if (isEmptyValue(value)) {
600 PropertyTypeValidator validator = type.getValidator();
601 return validator.isValid(value, innerType, dataTypes);
605 public boolean isEmptyValue(String value) {
606 return value == null;