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 java.lang.reflect.Type;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
28 import java.util.Map.Entry;
29 import java.util.function.Supplier;
31 import javax.servlet.ServletContext;
33 import org.apache.commons.collections.CollectionUtils;
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.titan.TitanOperationStatus;
38 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
39 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
40 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
41 import org.openecomp.sdc.be.impl.ComponentsUtils;
42 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
43 import org.openecomp.sdc.be.model.DataTypeDefinition;
44 import org.openecomp.sdc.be.model.IComplexDefaultValue;
45 import org.openecomp.sdc.be.model.PropertyConstraint;
46 import org.openecomp.sdc.be.model.PropertyDefinition;
47 import org.openecomp.sdc.be.model.Resource;
48 import org.openecomp.sdc.be.model.User;
49 import org.openecomp.sdc.be.model.jsontitan.operations.ToscaOperationFacade;
50 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
51 import org.openecomp.sdc.be.model.operations.api.IResourceOperation;
52 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
53 import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
54 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
55 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintDeserialiser;
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.DataTypeValidatorConverter;
60 import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
61 import org.openecomp.sdc.be.resources.data.EntryData;
62 import org.openecomp.sdc.be.resources.data.PropertyData;
63 import org.openecomp.sdc.common.api.Constants;
64 import org.openecomp.sdc.common.config.EcompErrorName;
65 import org.openecomp.sdc.exception.ResponseFormat;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68 import org.springframework.beans.factory.annotation.Autowired;
69 import org.springframework.stereotype.Component;
70 import org.springframework.web.context.WebApplicationContext;
72 import com.google.gson.Gson;
73 import com.google.gson.GsonBuilder;
74 import com.google.gson.JsonElement;
75 import com.google.gson.reflect.TypeToken;
77 import fj.data.Either;
79 @Component("propertyBusinessLogic")
80 public class PropertyBusinessLogic extends BaseBusinessLogic {
82 private static final String CREATE_PROPERTY = "CreateProperty";
84 private static Logger log = LoggerFactory.getLogger(PropertyBusinessLogic.class.getName());
86 private static final String EMPTY_VALUE = null;
88 private DataTypeValidatorConverter dataTypeValidatorConverter = DataTypeValidatorConverter.getInstance();
90 @javax.annotation.Resource
91 private IResourceOperation resourceOperation = null;
93 protected static IElementOperation getElementDao(Class<IElementOperation> class1, ServletContext context) {
94 WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
96 WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
98 return webApplicationContext.getBean(class1);
101 public Either<Map<String, DataTypeDefinition>, ResponseFormat> getAllDataTypes() {
102 Either<Map<String, DataTypeDefinition>, ResponseFormat> eitherAllDataTypes = getAllDataTypes(applicationDataTypeCache);
103 return eitherAllDataTypes;
107 * Create new property on resource in graph
110 * @param propertyName
111 * @param newPropertyDefinition
113 * @return Either<PropertyDefinition, ActionStatus>
115 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> createProperty(String resourceId, String propertyName, PropertyDefinition newPropertyDefinition, String userId) {
117 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
119 Either<User, ResponseFormat> resp = validateUserExists(userId, "create Property", false);
120 if (resp.isRight()) {
121 result = Either.right(resp.right().value());
125 StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
126 if (!lockResult.equals(StorageOperationStatus.OK)) {
127 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
128 log.info("Failed to lock component {}. Error - {}", resourceId, lockResult);
129 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
134 // Get the resource from DB
135 Either<Resource, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(resourceId);
136 if (status.isRight()) {
137 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
140 Resource resource = status.left().value();
142 // verify that resource is checked-out and the user is the last
144 if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
145 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
149 // verify property not exist in resource
150 List<PropertyDefinition> resourceProperties = resource.getProperties();
152 if (resourceProperties != null) {
153 if (isPropertyExist(resourceProperties, resourceId, propertyName, newPropertyDefinition.getType())) {
154 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_ALREADY_EXIST, propertyName));
159 Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
160 if (allDataTypes.isRight()) {
161 result = Either.right(allDataTypes.right().value());
165 Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
167 // validate property default values
168 Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
169 if (defaultValuesValidation.isRight()) {
170 result = Either.right(defaultValuesValidation.right().value());
174 ToscaPropertyType type = getType(newPropertyDefinition.getType());
176 PropertyValueConverter converter = type.getConverter();
178 String innerType = null;
179 if (newPropertyDefinition != null) {
180 SchemaDefinition schema = newPropertyDefinition.getSchema();
181 if (schema != null) {
182 PropertyDataDefinition prop = schema.getProperty();
184 innerType = prop.getType();
187 String convertedValue = null;
188 if (newPropertyDefinition.getDefaultValue() != null) {
189 convertedValue = converter.convert(newPropertyDefinition.getDefaultValue(), innerType, allDataTypes.left().value());
190 newPropertyDefinition.setDefaultValue(convertedValue);
195 // add the new property to resource on graph
196 // need to get StorageOpaerationStatus and convert to ActionStatus
197 // from componentsUtils
198 Either<PropertyDefinition, StorageOperationStatus> either = toscaOperationFacade.addPropertyToResource(propertyName, newPropertyDefinition, resource);
199 if (either.isRight()) {
200 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
204 PropertyDefinition createdPropertyDefinition = either.left().value();
205 EntryData<String, PropertyDefinition> property = new EntryData<String, PropertyDefinition>(propertyName, createdPropertyDefinition);
206 result = Either.left(property);
210 commitOrRollback(result);
212 graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
218 * Get property of resource
225 public Either<Entry<String, PropertyDefinition>, ResponseFormat> getProperty(String resourceId, String propertyId, String userId) {
227 Either<User, ResponseFormat> resp = validateUserExists(userId, "create Component Instance", false);
228 if (resp.isRight()) {
229 return Either.right(resp.right().value());
232 // Get the resource from DB
233 Either<Resource, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(resourceId);
234 if (status.isRight()) {
235 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
237 Resource resource = status.left().value();
239 // verify property exist in resource
240 List<PropertyDefinition> properties = resource.getProperties();
241 if (properties == null) {
242 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
244 for (PropertyDefinition property : properties) {
245 if (property.getUniqueId().equals(propertyId) && isPropertyBelongsToResource(property, resourceId)) {
246 Map<String, PropertyDefinition> propMap = new HashMap<>();
247 propMap.put(property.getName(), property);
248 return Either.left(propMap.entrySet().iterator().next());
251 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
254 private boolean isPropertyBelongsToResource(PropertyDataDefinition property, String resourceId) {
255 return property.getOwnerId() == null || property.getOwnerId().equals(resourceId);
259 * delete property of resource from graph
266 public Either<Entry<String, PropertyDefinition>, ResponseFormat> deleteProperty(String resourceId, String propertyId, String userId) {
268 Either<Entry<String, PropertyDefinition>, ResponseFormat> result = null;
270 Either<User, ResponseFormat> resp = validateUserExists(userId, "delete Property", false);
271 if (resp.isRight()) {
272 return Either.right(resp.right().value());
275 StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
276 if (!lockResult.equals(StorageOperationStatus.OK)) {
277 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
278 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
284 // Get the resource from DB
285 Either<Resource, StorageOperationStatus> getResourceRes = toscaOperationFacade.getToscaElement(resourceId);
286 if (getResourceRes.isRight()) {
287 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
290 Resource resource = getResourceRes.left().value();
292 // verify that resource is checked-out and the user is the last
294 if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
295 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
299 // verify property exist in resource
300 Either<Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty = getProperty(resourceId, propertyId, userId);
301 if (statusGetProperty.isRight()) {
302 result = Either.right(statusGetProperty.right().value());
306 StorageOperationStatus status = toscaOperationFacade.deletePropertyOfResource(resource, statusGetProperty.left().value().getKey());
307 if (status != StorageOperationStatus.OK) {
308 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status), resource.getName()));
311 // propertyOperation.getTitanGenericDao().commit();
312 result = Either.left(statusGetProperty.left().value());
316 commitOrRollback(result);
318 graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
327 * @param newPropertyDefinition
331 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> updateProperty(String resourceId, String propertyId, PropertyDefinition newPropertyDefinition, String userId) {
333 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
335 Either<Resource, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(resourceId);
336 if (status.isRight()) {
337 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
339 Resource resource = status.left().value();
341 if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
342 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
345 StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
346 if (!lockResult.equals(StorageOperationStatus.OK)) {
347 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
348 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
353 Either<Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty = getProperty(resourceId, propertyId, userId);
354 if (statusGetProperty.isRight()) {
355 result = Either.right(statusGetProperty.right().value());
358 String propertyName = statusGetProperty.left().value().getKey();
360 Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
361 if (allDataTypes.isRight()) {
362 result = Either.right(allDataTypes.right().value());
365 Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
367 Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
368 if (defaultValuesValidation.isRight()) {
369 result = Either.right(defaultValuesValidation.right().value());
373 Either<PropertyDefinition, StorageOperationStatus> either = handleProperty(propertyId, newPropertyDefinition, dataTypes);
374 if (either.isRight()) {
375 log.debug("Problem while updating property with id {}. Reason - {}", propertyId, either.right().value());
376 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
381 either = toscaOperationFacade.updatePropertyOfResource(resource, newPropertyDefinition);
382 if (either.isRight()) {
383 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
387 EntryData<String, PropertyDefinition> property = new EntryData<String, PropertyDefinition>(propertyName, either.left().value());
388 result = Either.left(property);
392 commitOrRollback(result);
393 graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
398 private boolean isPropertyExist(List<PropertyDefinition> properties, String resourceUid, String propertyName, String propertyType) {
399 boolean result = false;
400 if (!CollectionUtils.isEmpty(properties)) {
401 for (PropertyDefinition propertyDefinition : properties) {
403 if ( propertyDefinition.getName().equals(propertyName) &&
404 (propertyDefinition.getParentUniqueId().equals(resourceUid) || !propertyDefinition.getType().equals(propertyType)) ) {
413 private PropertyDefinition convertPropertyDataToPropertyDefinition(PropertyData propertyDataResult, String propertyName, String resourceId) {
414 log.debug("The object returned after create property is {}", propertyDataResult);
415 PropertyDefinition propertyDefResult = new PropertyDefinition(propertyDataResult.getPropertyDataDefinition());
416 propertyDefResult.setConstraints(convertConstraints(propertyDataResult.getConstraints()));
417 propertyDefResult.setName(propertyName);
418 propertyDefResult.setParentUniqueId(resourceId);
419 return propertyDefResult;
422 private List<PropertyConstraint> convertConstraints(List<String> constraints) {
424 if (constraints == null || constraints.size() == 0) {
428 List<PropertyConstraint> list = new ArrayList<PropertyConstraint>();
429 Type constraintType = new TypeToken<PropertyConstraint>() {
432 Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintDeserialiser()).create();
434 for (String constraintJson : constraints) {
435 PropertyConstraint propertyConstraint = gson.fromJson(constraintJson, constraintType);
436 list.add(propertyConstraint);
442 private Either<PropertyDefinition, StorageOperationStatus> handleProperty(String propertyId, PropertyDefinition newPropertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
444 StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(newPropertyDefinition, dataTypes);
445 if (validateAndUpdateProperty != StorageOperationStatus.OK) {
446 return Either.right(validateAndUpdateProperty);
449 return Either.left(newPropertyDefinition);
452 private StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
454 log.trace("Going to validate property type and value. {}", propertyDefinition);
456 String propertyType = propertyDefinition.getType();
457 String value = propertyDefinition.getDefaultValue();
459 ToscaPropertyType type = getType(propertyType);
463 DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
464 if (dataTypeDefinition == null) {
465 log.debug("The type {} of property cannot be found.", propertyType);
466 return StorageOperationStatus.INVALID_TYPE;
469 StorageOperationStatus status = validateAndUpdateComplexValue(propertyDefinition, propertyType, value, dataTypeDefinition, dataTypes);
474 String innerType = null;
476 Either<String, TitanOperationStatus> checkInnerType = getInnerType(type, () -> propertyDefinition.getSchema());
477 if (checkInnerType.isRight()) {
478 return StorageOperationStatus.INVALID_TYPE;
480 innerType = checkInnerType.left().value();
482 log.trace("After validating property type {}", propertyType);
484 boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
485 if (false == isValidProperty) {
486 log.info("The value {} of property from type {} is invalid", value, type);
487 return StorageOperationStatus.INVALID_VALUE;
490 PropertyValueConverter converter = type.getConverter();
492 if (isEmptyValue(value)) {
493 log.debug("Default value was not sent for property {}. Set default value to {}", propertyDefinition.getName(), EMPTY_VALUE);
494 propertyDefinition.setDefaultValue(EMPTY_VALUE);
495 } else if (false == isEmptyValue(value)) {
496 String convertedValue = converter.convert(value, innerType, dataTypes);
497 propertyDefinition.setDefaultValue(convertedValue);
499 return StorageOperationStatus.OK;
502 protected StorageOperationStatus validateAndUpdateComplexValue(IComplexDefaultValue propertyDefinition, String propertyType,
504 String value, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> dataTypes) {
506 ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
508 if (validateResult.right.booleanValue() == false) {
509 log.debug("The value {} of property from type {} is invalid", propertyType, propertyType);
510 return StorageOperationStatus.INVALID_VALUE;
513 JsonElement jsonElement = validateResult.left;
515 log.trace("Going to update value in property definition {} {}" , propertyDefinition.getName() , (jsonElement != null ? jsonElement.toString() : null));
517 updateValue(propertyDefinition, jsonElement);
519 return StorageOperationStatus.OK;
522 protected void updateValue(IComplexDefaultValue propertyDefinition, JsonElement jsonElement) {
524 propertyDefinition.setDefaultValue(getValueFromJsonElement(jsonElement));
528 protected String getValueFromJsonElement(JsonElement jsonElement) {
531 if (jsonElement == null || jsonElement.isJsonNull()) {
534 if (jsonElement.toString().isEmpty()) {
537 value = jsonElement.toString();
544 protected Either<String, TitanOperationStatus> getInnerType(ToscaPropertyType type, Supplier<SchemaDefinition> schemeGen) {
545 String innerType = null;
546 if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
548 SchemaDefinition def = schemeGen.get();// propDataDef.getSchema();
550 log.debug("Schema doesn't exists for property of type {}", type);
551 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
553 PropertyDataDefinition propDef = def.getProperty();
554 if (propDef == null) {
555 log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
556 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
558 innerType = propDef.getType();
560 return Either.left(innerType);
562 protected boolean isValidValue(ToscaPropertyType type, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
563 if (isEmptyValue(value)) {
567 PropertyTypeValidator validator = type.getValidator();
569 boolean isValid = validator.isValid(value, innerType, dataTypes);
570 if (true == isValid) {
578 public boolean isEmptyValue(String value) {