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.lang3.tuple.ImmutablePair;
27 import org.openecomp.sdc.be.config.BeEcompErrorManager;
28 import org.openecomp.sdc.be.dao.api.ActionStatus;
29 import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
30 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
31 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
32 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
33 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
34 import org.openecomp.sdc.be.model.DataTypeDefinition;
35 import org.openecomp.sdc.be.model.IComplexDefaultValue;
36 import org.openecomp.sdc.be.model.PropertyDefinition;
37 import org.openecomp.sdc.be.model.Resource;
38 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
39 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
40 import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
41 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
42 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
43 import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
44 import org.openecomp.sdc.be.resources.data.EntryData;
45 import org.openecomp.sdc.common.api.Constants;
46 import org.openecomp.sdc.common.log.wrappers.Logger;
47 import org.openecomp.sdc.exception.ResponseFormat;
48 import org.springframework.stereotype.Component;
49 import org.springframework.web.context.WebApplicationContext;
51 import javax.servlet.ServletContext;
52 import java.util.HashMap;
53 import java.util.List;
55 import java.util.Map.Entry;
56 import java.util.function.Supplier;
58 @Component("propertyBusinessLogic")
59 public class PropertyBusinessLogic extends BaseBusinessLogic {
61 private static final String CREATE_PROPERTY = "CreateProperty";
63 private static final Logger log = Logger.getLogger(PropertyBusinessLogic.class);
65 private static final String EMPTY_VALUE = null;
67 protected static IElementOperation getElementDao(Class<IElementOperation> class1, ServletContext context) {
68 WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
70 WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
72 return webApplicationContext.getBean(class1);
75 public Either<Map<String, DataTypeDefinition>, ResponseFormat> getAllDataTypes() {
76 return getAllDataTypes(applicationDataTypeCache);
80 * Create new property on resource in graph
84 * @param newPropertyDefinition
86 * @return either properties or response format
88 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> createProperty(String resourceId, String propertyName, PropertyDefinition newPropertyDefinition, String userId) {
90 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
92 validateUserExists(userId, "create Property", false);
94 StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
95 if (!lockResult.equals(StorageOperationStatus.OK)) {
96 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
97 log.info("Failed to lock component {}. Error - {}", resourceId, lockResult);
98 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
103 // Get the resource from DB
104 Either<Resource, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(resourceId);
105 if (status.isRight()) {
106 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
109 Resource resource = status.left().value();
111 // verify that resource is checked-out and the user is the last
113 if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
114 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
118 // verify property not exist in resource
119 List<PropertyDefinition> resourceProperties = resource.getProperties();
121 if (resourceProperties != null && isPropertyExist(resourceProperties, resourceId, propertyName, newPropertyDefinition.getType())) {
122 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_ALREADY_EXIST, propertyName));
126 Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
127 if (allDataTypes.isRight()) {
128 result = Either.right(allDataTypes.right().value());
132 Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
134 // validate property default values
135 Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
136 if (defaultValuesValidation.isRight()) {
137 result = Either.right(defaultValuesValidation.right().value());
140 convertProperty(newPropertyDefinition, allDataTypes);
143 // add the new property to resource on graph
144 // need to get StorageOpaerationStatus and convert to ActionStatus
145 // from componentsUtils
146 Either<PropertyDefinition, StorageOperationStatus> either = toscaOperationFacade.addPropertyToResource(propertyName, newPropertyDefinition, resource);
147 if (either.isRight()) {
148 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
152 PropertyDefinition createdPropertyDefinition = either.left().value();
153 EntryData<String, PropertyDefinition> property = new EntryData<>(propertyName, createdPropertyDefinition);
154 result = Either.left(property);
158 commitOrRollback(result);
160 graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
165 private void convertProperty(PropertyDefinition newPropertyDefinition, Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes) {
166 ToscaPropertyType type = getType(newPropertyDefinition.getType());
168 String innerType = null;
169 SchemaDefinition schema = newPropertyDefinition.getSchema();
170 if (schema != null && schema.getProperty() != null) {
171 innerType = schema.getProperty().getType();
173 if (newPropertyDefinition.getDefaultValue() != null) {
174 newPropertyDefinition.setDefaultValue(
175 type.getConverter().convert(
176 newPropertyDefinition.getDefaultValue(), innerType, allDataTypes.left().value()));
182 * Get property of resource
187 * @return either properties or response format
189 public Either<Entry<String, PropertyDefinition>, ResponseFormat> getProperty(String resourceId, String propertyId, String userId) {
191 validateUserExists(userId, "create Component Instance", false);
192 // Get the resource from DB
193 Either<Resource, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(resourceId);
194 if (status.isRight()) {
195 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
197 Resource resource = status.left().value();
199 // verify property exist in resource
200 List<PropertyDefinition> properties = resource.getProperties();
201 if (properties == null) {
202 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
204 for (PropertyDefinition property : properties) {
205 if (property.getUniqueId().equals(propertyId) ) {
206 Map<String, PropertyDefinition> propMap = new HashMap<>();
207 propMap.put(property.getName(), property);
208 return Either.left(propMap.entrySet().iterator().next());
211 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
215 * delete property of resource from graph
220 * @return either properties or response format
222 public Either<Entry<String, PropertyDefinition>, ResponseFormat> deleteProperty(String resourceId, String propertyId, String userId) {
224 Either<Entry<String, PropertyDefinition>, ResponseFormat> result = null;
226 validateUserExists(userId, "delete Property", false);
228 StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
229 if (!lockResult.equals(StorageOperationStatus.OK)) {
230 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
231 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
237 // Get the resource from DB
238 Either<Resource, StorageOperationStatus> getResourceRes = toscaOperationFacade.getToscaElement(resourceId);
239 if (getResourceRes.isRight()) {
240 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
243 Resource resource = getResourceRes.left().value();
245 // verify that resource is checked-out and the user is the last
247 if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
248 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
252 // verify property exist in resource
253 Either<Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty = getProperty(resourceId, propertyId, userId);
254 if (statusGetProperty.isRight()) {
255 result = Either.right(statusGetProperty.right().value());
259 StorageOperationStatus status = toscaOperationFacade.deletePropertyOfResource(resource, statusGetProperty.left().value().getKey());
260 if (status != StorageOperationStatus.OK) {
261 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status), resource.getName()));
264 result = Either.left(statusGetProperty.left().value());
268 commitOrRollback(result);
270 graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
279 * @param newPropertyDefinition
281 * @return either properties or response format
283 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> updateProperty(String resourceId, String propertyId, PropertyDefinition newPropertyDefinition, String userId) {
285 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
287 Either<Resource, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(resourceId);
288 if (status.isRight()) {
289 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
291 Resource resource = status.left().value();
293 if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
294 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
297 StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
298 if (!lockResult.equals(StorageOperationStatus.OK)) {
299 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
300 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
305 Either<Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty = getProperty(resourceId, propertyId, userId);
306 if (statusGetProperty.isRight()) {
307 result = Either.right(statusGetProperty.right().value());
310 String propertyName = statusGetProperty.left().value().getKey();
312 Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
313 if (allDataTypes.isRight()) {
314 result = Either.right(allDataTypes.right().value());
317 Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
319 Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
320 if (defaultValuesValidation.isRight()) {
321 result = Either.right(defaultValuesValidation.right().value());
325 Either<PropertyDefinition, StorageOperationStatus> either = handleProperty(newPropertyDefinition, dataTypes);
326 if (either.isRight()) {
327 log.debug("Problem while updating property with id {}. Reason - {}", propertyId, either.right().value());
328 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
333 either = toscaOperationFacade.updatePropertyOfResource(resource, newPropertyDefinition);
334 if (either.isRight()) {
335 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
339 EntryData<String, PropertyDefinition> property = new EntryData<>(propertyName, either.left().value());
340 result = Either.left(property);
344 commitOrRollback(result);
345 graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
350 private boolean isPropertyExist(List<PropertyDefinition> properties, String resourceUid, String propertyName, String propertyType) {
351 boolean result = false;
352 if (!CollectionUtils.isEmpty(properties)) {
353 for (PropertyDefinition propertyDefinition : properties) {
355 if ( propertyDefinition.getName().equals(propertyName) &&
356 (propertyDefinition.getParentUniqueId().equals(resourceUid) || !propertyDefinition.getType().equals(propertyType)) ) {
365 private Either<PropertyDefinition, StorageOperationStatus> handleProperty(PropertyDefinition newPropertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
367 StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(newPropertyDefinition, dataTypes);
368 if (validateAndUpdateProperty != StorageOperationStatus.OK) {
369 return Either.right(validateAndUpdateProperty);
372 return Either.left(newPropertyDefinition);
375 private StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
377 log.trace("Going to validate property type and value. {}", propertyDefinition);
379 String propertyType = propertyDefinition.getType();
380 String value = propertyDefinition.getDefaultValue();
382 ToscaPropertyType type = getType(propertyType);
385 DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
386 if (dataTypeDefinition == null) {
387 log.debug("The type {} of property cannot be found.", propertyType);
388 return StorageOperationStatus.INVALID_TYPE;
390 return validateAndUpdateComplexValue(propertyDefinition, propertyType, value, dataTypeDefinition, dataTypes);
394 Either<String, TitanOperationStatus> checkInnerType = getInnerType(type, propertyDefinition::getSchema);
395 if (checkInnerType.isRight()) {
396 return StorageOperationStatus.INVALID_TYPE;
398 innerType = checkInnerType.left().value();
400 log.trace("After validating property type {}", propertyType);
402 boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
403 if (!isValidProperty) {
404 log.info("The value {} of property from type {} is invalid", value, type);
405 return StorageOperationStatus.INVALID_VALUE;
408 PropertyValueConverter converter = type.getConverter();
410 if (isEmptyValue(value)) {
411 log.debug("Default value was not sent for property {}. Set default value to {}", propertyDefinition.getName(), EMPTY_VALUE);
412 propertyDefinition.setDefaultValue(EMPTY_VALUE);
413 } else if (!isEmptyValue(value)) {
414 String convertedValue = converter.convert(value, innerType, dataTypes);
415 propertyDefinition.setDefaultValue(convertedValue);
417 return StorageOperationStatus.OK;
420 private StorageOperationStatus validateAndUpdateComplexValue(IComplexDefaultValue propertyDefinition, String propertyType,
421 String value, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> dataTypes) {
423 ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
425 if (validateResult.right) {
426 log.debug("The value {} of property from type {} is invalid", propertyType, propertyType);
427 return StorageOperationStatus.INVALID_VALUE;
430 JsonElement jsonElement = validateResult.left;
432 log.trace("Going to update value in property definition {} {}" , propertyDefinition.getName() , jsonElement);
434 updateValue(propertyDefinition, jsonElement);
436 return StorageOperationStatus.OK;
439 private void updateValue(IComplexDefaultValue propertyDefinition, JsonElement jsonElement) {
441 propertyDefinition.setDefaultValue(getValueFromJsonElement(jsonElement));
446 protected String getValueFromJsonElement(JsonElement jsonElement) {
447 if (jsonElement == null || jsonElement.isJsonNull()) {
450 if(jsonElement.toString().isEmpty()){
453 return jsonElement.toString();
456 private Either<String, TitanOperationStatus> getInnerType(ToscaPropertyType type, Supplier<SchemaDefinition> schemeGen) {
457 String innerType = null;
458 if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
460 SchemaDefinition def = schemeGen.get();
462 log.debug("Schema doesn't exists for property of type {}", type);
463 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
465 PropertyDataDefinition propDef = def.getProperty();
466 if (propDef == null) {
467 log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
468 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
470 innerType = propDef.getType();
472 return Either.left(innerType);
476 protected boolean isValidValue(ToscaPropertyType type, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
477 if (isEmptyValue(value)) {
480 PropertyTypeValidator validator = type.getValidator();
481 return validator.isValid(value, innerType, dataTypes);
485 public boolean isEmptyValue(String value) {
486 return value == null;