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 java.util.ArrayList;
26 import java.util.List;
28 import java.util.Objects;
29 import java.util.Optional;
30 import java.util.function.Supplier;
31 import javax.servlet.ServletContext;
32 import org.apache.commons.collections.CollectionUtils;
33 import org.apache.commons.collections.MapUtils;
34 import org.apache.commons.lang3.tuple.ImmutablePair;
35 import org.openecomp.sdc.be.config.BeEcompErrorManager;
36 import org.openecomp.sdc.be.dao.api.ActionStatus;
37 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
38 import org.openecomp.sdc.be.datatypes.elements.ListDataDefinition;
39 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
40 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
41 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
42 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
43 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
44 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
45 import org.openecomp.sdc.be.model.Component;
46 import org.openecomp.sdc.be.model.ComponentInstanceInterface;
47 import org.openecomp.sdc.be.model.ComponentParametersView;
48 import org.openecomp.sdc.be.model.DataTypeDefinition;
49 import org.openecomp.sdc.be.model.IComplexDefaultValue;
50 import org.openecomp.sdc.be.model.InterfaceDefinition;
51 import org.openecomp.sdc.be.model.PropertyDefinition;
52 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
53 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
54 import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
55 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
56 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
57 import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
58 import org.openecomp.sdc.be.resources.data.EntryData;
59 import org.openecomp.sdc.common.api.Constants;
60 import org.openecomp.sdc.common.log.wrappers.Logger;
61 import org.openecomp.sdc.exception.ResponseFormat;
62 import org.springframework.web.context.WebApplicationContext;
64 @org.springframework.stereotype.Component("propertyBusinessLogic")
65 public class PropertyBusinessLogic extends BaseBusinessLogic {
67 private static final String CREATE_PROPERTY = "CreateProperty";
69 private static final Logger log = Logger.getLogger(PropertyBusinessLogic.class);
71 private static final String EMPTY_VALUE = null;
73 protected static IElementOperation getElementDao(Class<IElementOperation> class1, ServletContext context) {
74 WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
76 WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
78 return webApplicationContext.getBean(class1);
81 public Either<Map<String, DataTypeDefinition>, ResponseFormat> getAllDataTypes() {
82 return getAllDataTypes(applicationDataTypeCache);
86 * Create new property on component in graph
90 * @param newPropertyDefinition
92 * @return either properties or response format
95 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> addPropertyToComponent(String componentId,
97 PropertyDefinition newPropertyDefinition,
99 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
101 validateUserExists(userId, "create Property", false);
103 Either<Component, StorageOperationStatus> serviceElement =
104 toscaOperationFacade.getToscaElement(componentId);
105 if (serviceElement.isRight()) {
106 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
109 Component component = serviceElement.left().value();
110 NodeTypeEnum nodeType = component.getComponentType().getNodeType();
111 StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType );
112 if (!lockResult.equals(StorageOperationStatus.OK)) {
113 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(), componentId);
114 log.info("Failed to lock component {}. Error - {}", componentId, lockResult);
115 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
120 if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
121 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
125 List<PropertyDefinition> properties = component.getProperties();
127 if(CollectionUtils.isEmpty(properties)) {
128 properties = new ArrayList<>();
131 if(isPropertyExistInComponent(properties, propertyName)) {
134 Either.right(componentsUtils.getResponseFormat(ActionStatus
135 .PROPERTY_ALREADY_EXIST, propertyName));
140 Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
141 if (allDataTypes.isRight()) {
142 result = Either.right(allDataTypes.right().value());
146 Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
148 // validate property default values
149 Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
150 if (defaultValuesValidation.isRight()) {
151 result = Either.right(defaultValuesValidation.right().value());
155 ToscaPropertyType type = getType(newPropertyDefinition.getType());
157 PropertyValueConverter converter = type.getConverter();
159 String innerType = null;
160 if (newPropertyDefinition != null) {
161 SchemaDefinition schema = newPropertyDefinition.getSchema();
162 if (schema != null) {
163 PropertyDataDefinition prop = schema.getProperty();
165 innerType = prop.getType();
168 String convertedValue = null;
169 if (newPropertyDefinition.getDefaultValue() != null) {
170 convertedValue = converter.convert(
171 newPropertyDefinition.getDefaultValue(), innerType, allDataTypes.left().value());
172 newPropertyDefinition.setDefaultValue(convertedValue);
176 Either<PropertyDefinition, StorageOperationStatus> addPropertyEither =
178 .addPropertyToComponent(propertyName, newPropertyDefinition, component);
180 if (addPropertyEither.isRight()) {
181 log.info("Failed to add new property {}. Error - {}", componentId,
182 addPropertyEither.right().value());
183 result = Either.right(componentsUtils.getResponseFormat(ActionStatus
189 result = Either.left(new EntryData<>(propertyName, newPropertyDefinition));
193 commitOrRollback(result);
195 graphLockOperation.unlockComponent(componentId, nodeType);
201 * Get property of component
206 * @return either properties or response format
209 public Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> getComponentProperty(String componentId, String propertyId, String userId) {
211 validateUserExists(userId, "create Component Instance", false);
212 // Get the resource from DB
213 Either<Component, StorageOperationStatus> status =
214 toscaOperationFacade.getToscaElement(componentId);
215 if (status.isRight()) {
216 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
218 Component component = status.left().value();
219 List<PropertyDefinition> properties = component.getProperties();
220 if(CollectionUtils.isEmpty(properties)) {
221 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
224 for(PropertyDefinition property : properties) {
225 if(property.getUniqueId().equals(propertyId)) {
226 return Either.left(new EntryData<>(property.getName(), property));
229 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
233 public Either<List<PropertyDefinition>, ResponseFormat> getPropertiesList(String componentId,
235 validateUserExists(userId, "create Component Instance", false);
237 // Get the resource from DB
238 ComponentParametersView filter = new ComponentParametersView(true);
239 filter.setIgnoreProperties(false);
240 Either<Component, StorageOperationStatus> status =
241 toscaOperationFacade.getToscaElement(componentId);
242 if (status.isRight()) {
243 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
245 Component component = status.left().value();
246 List<PropertyDefinition> properties = component.getProperties();
248 return Either.left(properties);
253 * delete property of component from graph
258 * @return either properties or response format
261 public Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> deletePropertyFromComponent(String componentId, String propertyId, String userId) {
263 Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> result = null;
265 validateUserExists(userId, "delete Property", false);
267 // Get the resource from DB
268 Either<Component, StorageOperationStatus> getComponentRes = toscaOperationFacade.getToscaElement(componentId);
269 if (getComponentRes.isRight()) {
270 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
273 Component component = getComponentRes.left().value();
274 NodeTypeEnum nodeType = component.getComponentType().getNodeType();
275 StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
276 if (!lockResult.equals(StorageOperationStatus.OK)) {
277 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(),
279 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
284 // verify that resource is checked-out and the user is the last
286 if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
287 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
291 // verify property exist in resource
292 Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty =
293 getComponentProperty(componentId, propertyId, userId);
294 if (statusGetProperty.isRight()) {
295 result = Either.right(statusGetProperty.right().value());
299 Map.Entry<String, PropertyDefinition> propertyDefinitionEntry = statusGetProperty.left().value();
301 // verify that the property is not used by operation
302 if (isPropertyUsedByOperation(component, propertyDefinitionEntry.getValue())) {
303 return Either.right(componentsUtils.getResponseFormat(ActionStatus
304 .PROPERTY_USED_BY_OPERATION));
307 StorageOperationStatus status =
308 toscaOperationFacade.deletePropertyOfComponent(component, propertyDefinitionEntry.getKey());
309 if (status != StorageOperationStatus.OK) {
310 result = Either.right(componentsUtils.getResponseFormat(componentsUtils
311 .convertFromStorageResponse(status), component.getName()));
314 result = Either.left(propertyDefinitionEntry);
318 commitOrRollback(result);
320 graphLockOperation.unlockComponent(componentId, nodeType);
324 public boolean isPropertyUsedByOperation(Component component,
325 PropertyDefinition propertyDefinitionEntry) {
327 // Component's own interfaces
328 Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
329 if(MapUtils.isNotEmpty(interfaces)){
330 for(Map.Entry<String, InterfaceDefinition> interfaceEntry : interfaces.entrySet()) {
331 if (isPropertyExistInOperationInterface(propertyDefinitionEntry, interfaceEntry.getValue())) {
337 // Component's child's component interfaces
338 if(isPropertyUsedInCIInterfaces(component.getComponentInstancesInterfaces(), propertyDefinitionEntry)){
342 // Component's parent's component interfaces
343 Either<List<Component>, StorageOperationStatus> componentList = toscaOperationFacade.getParentComponents(component.getUniqueId());
344 if(componentList.isLeft()){
345 for (Component parentComponent : componentList.left().value()) {
346 if(isPropertyUsedInCIInterfaces(parentComponent.getComponentInstancesInterfaces(), propertyDefinitionEntry)){
355 private boolean isPropertyUsedInCIInterfaces(Map<String, List<ComponentInstanceInterface>> componentInstanceInterfaces, PropertyDefinition propertyDefinitionEntry){
356 Optional<ComponentInstanceInterface> isPropertyExistInOperationInterface = Optional.empty();
357 if(MapUtils.isNotEmpty(componentInstanceInterfaces)){
358 isPropertyExistInOperationInterface = componentInstanceInterfaces.entrySet().stream()
359 .flatMap(interfaceEntry -> interfaceEntry.getValue().stream())
360 .filter(instanceInterface -> isPropertyExistInOperationInterface(propertyDefinitionEntry, instanceInterface))
363 return isPropertyExistInOperationInterface.isPresent();
366 private boolean isPropertyExistInOperationInterface(PropertyDefinition propertyDefinition,
367 InterfaceDefinition interfaceDefinition) {
368 Map<String, OperationDataDefinition> operations =
369 interfaceDefinition.getOperations();
370 for(Map.Entry<String, OperationDataDefinition> operationEntry : operations
372 Optional<OperationInputDefinition> inputWithDeletedPropertyCandidate =
373 getInputWithDeclaredProperty(propertyDefinition, operationEntry);
375 if(inputWithDeletedPropertyCandidate.isPresent()) {
382 private Optional<OperationInputDefinition> getInputWithDeclaredProperty(PropertyDefinition propertyDefinition,
383 Map.Entry<String, OperationDataDefinition> operationEntry) {
384 ListDataDefinition<OperationInputDefinition> inputs =
385 operationEntry.getValue().getInputs();
386 List<OperationInputDefinition> operationInputsList =
387 Objects.isNull(inputs) ? null : inputs.getListToscaDataDefinition();
389 if(CollectionUtils.isEmpty(operationInputsList)) {
390 return Optional.empty();
393 return operationInputsList.stream().filter(input -> input.getInputId().equals(propertyDefinition.getUniqueId())
394 || (input.getSourceProperty() != null && input.getSourceProperty().equals(propertyDefinition.getUniqueId()))).findAny();
402 * @param newPropertyDefinition
404 * @return either properties or response format
407 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> updateComponentProperty(String componentId,
409 PropertyDefinition newPropertyDefinition,
412 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
414 Either<Component, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(
416 if (status.isRight()) {
417 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
419 Component component = status.left().value();
420 NodeTypeEnum nodeType = component.getComponentType().getNodeType();
422 if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
423 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
426 StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
427 if (!lockResult.equals(StorageOperationStatus.OK)) {
428 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(),
430 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
435 Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty =
436 getComponentProperty(componentId, propertyId, userId);
437 if (statusGetProperty.isRight()) {
438 result = Either.right(statusGetProperty.right().value());
441 String propertyName = statusGetProperty.left().value().getKey();
443 Either<PropertyDefinition, StorageOperationStatus> either =
444 toscaOperationFacade.updatePropertyOfComponent(component, newPropertyDefinition);
445 if (either.isRight()) {
446 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), component.getName()));
450 EntryData<String, PropertyDefinition> property = new EntryData<>(propertyName, either.left().value());
451 result = Either.left(property);
455 commitOrRollback(result);
456 graphLockOperation.unlockComponent(componentId, nodeType);
461 private boolean isPropertyExistInComponent(List<PropertyDefinition> properties, String propertyName) {
462 if(CollectionUtils.isEmpty(properties)) {
466 Optional<PropertyDefinition> propertyCandidate =
467 properties.stream().filter(property -> property.getName().equals(propertyName))
470 return propertyCandidate.isPresent();
473 private StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
475 log.trace("Going to validate property type and value. {}", propertyDefinition);
477 String propertyType = propertyDefinition.getType();
478 String value = propertyDefinition.getDefaultValue();
480 ToscaPropertyType type = getType(propertyType);
483 DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
484 if (dataTypeDefinition == null) {
485 log.debug("The type {} of property cannot be found.", propertyType);
486 return StorageOperationStatus.INVALID_TYPE;
488 return validateAndUpdateComplexValue(propertyDefinition, propertyType, value, dataTypeDefinition, dataTypes);
492 Either<String, JanusGraphOperationStatus> checkInnerType = getInnerType(type, propertyDefinition::getSchema);
493 if (checkInnerType.isRight()) {
494 return StorageOperationStatus.INVALID_TYPE;
496 innerType = checkInnerType.left().value();
498 log.trace("After validating property type {}", propertyType);
500 boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
501 if (!isValidProperty) {
502 log.info("The value {} of property from type {} is invalid", value, type);
503 return StorageOperationStatus.INVALID_VALUE;
506 PropertyValueConverter converter = type.getConverter();
508 if (isEmptyValue(value)) {
509 log.debug("Default value was not sent for property {}. Set default value to {}", propertyDefinition.getName(), EMPTY_VALUE);
510 propertyDefinition.setDefaultValue(EMPTY_VALUE);
511 } else if (!isEmptyValue(value)) {
512 String convertedValue = converter.convert(value, innerType, dataTypes);
513 propertyDefinition.setDefaultValue(convertedValue);
515 return StorageOperationStatus.OK;
518 private StorageOperationStatus validateAndUpdateComplexValue(IComplexDefaultValue propertyDefinition, String propertyType,
519 String value, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> dataTypes) {
521 ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
523 if (validateResult.right) {
524 log.debug("The value {} of property from type {} is invalid", propertyType, propertyType);
525 return StorageOperationStatus.INVALID_VALUE;
528 JsonElement jsonElement = validateResult.left;
530 log.trace("Going to update value in property definition {} {}" , propertyDefinition.getName() , jsonElement);
532 updateValue(propertyDefinition, jsonElement);
534 return StorageOperationStatus.OK;
537 private void updateValue(IComplexDefaultValue propertyDefinition, JsonElement jsonElement) {
539 propertyDefinition.setDefaultValue(getValueFromJsonElement(jsonElement));
544 protected String getValueFromJsonElement(JsonElement jsonElement) {
545 if (jsonElement == null || jsonElement.isJsonNull()) {
548 if(jsonElement.toString().isEmpty()){
551 return jsonElement.toString();
554 private Either<String, JanusGraphOperationStatus> getInnerType(ToscaPropertyType type, Supplier<SchemaDefinition> schemeGen) {
555 String innerType = null;
556 if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
558 SchemaDefinition def = schemeGen.get();
560 log.debug("Schema doesn't exists for property of type {}", type);
561 return Either.right(JanusGraphOperationStatus.ILLEGAL_ARGUMENT);
563 PropertyDataDefinition propDef = def.getProperty();
564 if (propDef == null) {
565 log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
566 return Either.right(JanusGraphOperationStatus.ILLEGAL_ARGUMENT);
568 innerType = propDef.getType();
570 return Either.left(innerType);
574 protected boolean isValidValue(ToscaPropertyType type, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
575 if (isEmptyValue(value)) {
578 PropertyTypeValidator validator = type.getValidator();
579 return validator.isValid(value, innerType, dataTypes);
583 public boolean isEmptyValue(String value) {
584 return value == null;