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.User;
39 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
40 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
41 import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
42 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
43 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
44 import org.openecomp.sdc.be.model.tosca.validators.DataTypeValidatorConverter;
45 import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
46 import org.openecomp.sdc.be.resources.data.EntryData;
47 import org.openecomp.sdc.common.api.Constants;
48 import org.openecomp.sdc.exception.ResponseFormat;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51 import org.springframework.stereotype.Component;
52 import org.springframework.web.context.WebApplicationContext;
54 import javax.servlet.ServletContext;
55 import java.util.HashMap;
56 import java.util.List;
58 import java.util.Map.Entry;
59 import java.util.function.Supplier;
61 @Component("propertyBusinessLogic")
62 public class PropertyBusinessLogic extends BaseBusinessLogic {
64 private static final String CREATE_PROPERTY = "CreateProperty";
66 private static final Logger log = LoggerFactory.getLogger(PropertyBusinessLogic.class);
68 private static final String EMPTY_VALUE = null;
70 private DataTypeValidatorConverter dataTypeValidatorConverter = DataTypeValidatorConverter.getInstance();
72 protected static IElementOperation getElementDao(Class<IElementOperation> class1, ServletContext context) {
73 WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
75 WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
77 return webApplicationContext.getBean(class1);
80 public Either<Map<String, DataTypeDefinition>, ResponseFormat> getAllDataTypes() {
81 Either<Map<String, DataTypeDefinition>, ResponseFormat> eitherAllDataTypes = getAllDataTypes(applicationDataTypeCache);
82 return eitherAllDataTypes;
86 * Create new property on resource in graph
90 * @param newPropertyDefinition
92 * @return Either<PropertyDefinition, ActionStatus>
94 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> createProperty(String resourceId, String propertyName, PropertyDefinition newPropertyDefinition, String userId) {
96 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
98 Either<User, ResponseFormat> resp = validateUserExists(userId, "create Property", false);
100 result = Either.right(resp.right().value());
104 StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
105 if (!lockResult.equals(StorageOperationStatus.OK)) {
106 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
107 log.info("Failed to lock component {}. Error - {}", resourceId, lockResult);
108 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
113 // Get the resource from DB
114 Either<Resource, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(resourceId);
115 if (status.isRight()) {
116 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
119 Resource resource = status.left().value();
121 // verify that resource is checked-out and the user is the last
123 if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
124 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
128 // verify property not exist in resource
129 List<PropertyDefinition> resourceProperties = resource.getProperties();
131 if (resourceProperties != null) {
132 if (isPropertyExist(resourceProperties, resourceId, propertyName, newPropertyDefinition.getType())) {
133 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_ALREADY_EXIST, propertyName));
138 Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
139 if (allDataTypes.isRight()) {
140 result = Either.right(allDataTypes.right().value());
144 Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
146 // validate property default values
147 Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
148 if (defaultValuesValidation.isRight()) {
149 result = Either.right(defaultValuesValidation.right().value());
153 ToscaPropertyType type = getType(newPropertyDefinition.getType());
155 PropertyValueConverter converter = type.getConverter();
157 String innerType = null;
158 if (newPropertyDefinition != null) {
159 SchemaDefinition schema = newPropertyDefinition.getSchema();
160 if (schema != null) {
161 PropertyDataDefinition prop = schema.getProperty();
163 innerType = prop.getType();
166 String convertedValue = null;
167 if (newPropertyDefinition.getDefaultValue() != null) {
168 convertedValue = converter.convert(newPropertyDefinition.getDefaultValue(), innerType, allDataTypes.left().value());
169 newPropertyDefinition.setDefaultValue(convertedValue);
174 // add the new property to resource on graph
175 // need to get StorageOpaerationStatus and convert to ActionStatus
176 // from componentsUtils
177 Either<PropertyDefinition, StorageOperationStatus> either = toscaOperationFacade.addPropertyToResource(propertyName, newPropertyDefinition, resource);
178 if (either.isRight()) {
179 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
183 PropertyDefinition createdPropertyDefinition = either.left().value();
184 EntryData<String, PropertyDefinition> property = new EntryData<String, PropertyDefinition>(propertyName, createdPropertyDefinition);
185 result = Either.left(property);
189 commitOrRollback(result);
191 graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
197 * Get property of resource
204 public Either<Entry<String, PropertyDefinition>, ResponseFormat> getProperty(String resourceId, String propertyId, String userId) {
206 Either<User, ResponseFormat> resp = validateUserExists(userId, "create Component Instance", false);
207 if (resp.isRight()) {
208 return Either.right(resp.right().value());
211 // Get the resource from DB
212 Either<Resource, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(resourceId);
213 if (status.isRight()) {
214 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
216 Resource resource = status.left().value();
218 // verify property exist in resource
219 List<PropertyDefinition> properties = resource.getProperties();
220 if (properties == null) {
221 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
223 for (PropertyDefinition property : properties) {
224 if (property.getUniqueId().equals(propertyId) ) {
225 Map<String, PropertyDefinition> propMap = new HashMap<>();
226 propMap.put(property.getName(), property);
227 return Either.left(propMap.entrySet().iterator().next());
230 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
234 * delete property of resource from graph
241 public Either<Entry<String, PropertyDefinition>, ResponseFormat> deleteProperty(String resourceId, String propertyId, String userId) {
243 Either<Entry<String, PropertyDefinition>, ResponseFormat> result = null;
245 Either<User, ResponseFormat> resp = validateUserExists(userId, "delete Property", false);
246 if (resp.isRight()) {
247 return Either.right(resp.right().value());
250 StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
251 if (!lockResult.equals(StorageOperationStatus.OK)) {
252 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
253 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
259 // Get the resource from DB
260 Either<Resource, StorageOperationStatus> getResourceRes = toscaOperationFacade.getToscaElement(resourceId);
261 if (getResourceRes.isRight()) {
262 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
265 Resource resource = getResourceRes.left().value();
267 // verify that resource is checked-out and the user is the last
269 if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
270 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
274 // verify property exist in resource
275 Either<Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty = getProperty(resourceId, propertyId, userId);
276 if (statusGetProperty.isRight()) {
277 result = Either.right(statusGetProperty.right().value());
281 StorageOperationStatus status = toscaOperationFacade.deletePropertyOfResource(resource, statusGetProperty.left().value().getKey());
282 if (status != StorageOperationStatus.OK) {
283 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status), resource.getName()));
286 result = Either.left(statusGetProperty.left().value());
290 commitOrRollback(result);
292 graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
301 * @param newPropertyDefinition
305 public Either<EntryData<String, PropertyDefinition>, ResponseFormat> updateProperty(String resourceId, String propertyId, PropertyDefinition newPropertyDefinition, String userId) {
307 Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
309 Either<Resource, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(resourceId);
310 if (status.isRight()) {
311 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
313 Resource resource = status.left().value();
315 if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
316 return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
319 StorageOperationStatus lockResult = graphLockOperation.lockComponent(resourceId, NodeTypeEnum.Resource);
320 if (!lockResult.equals(StorageOperationStatus.OK)) {
321 BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, NodeTypeEnum.Resource.name().toLowerCase(), resourceId);
322 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
327 Either<Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty = getProperty(resourceId, propertyId, userId);
328 if (statusGetProperty.isRight()) {
329 result = Either.right(statusGetProperty.right().value());
332 String propertyName = statusGetProperty.left().value().getKey();
334 Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
335 if (allDataTypes.isRight()) {
336 result = Either.right(allDataTypes.right().value());
339 Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
341 Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
342 if (defaultValuesValidation.isRight()) {
343 result = Either.right(defaultValuesValidation.right().value());
347 Either<PropertyDefinition, StorageOperationStatus> either = handleProperty(propertyId, newPropertyDefinition, dataTypes);
348 if (either.isRight()) {
349 log.debug("Problem while updating property with id {}. Reason - {}", propertyId, either.right().value());
350 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
355 either = toscaOperationFacade.updatePropertyOfResource(resource, newPropertyDefinition);
356 if (either.isRight()) {
357 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
361 EntryData<String, PropertyDefinition> property = new EntryData<String, PropertyDefinition>(propertyName, either.left().value());
362 result = Either.left(property);
366 commitOrRollback(result);
367 graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
372 private boolean isPropertyExist(List<PropertyDefinition> properties, String resourceUid, String propertyName, String propertyType) {
373 boolean result = false;
374 if (!CollectionUtils.isEmpty(properties)) {
375 for (PropertyDefinition propertyDefinition : properties) {
377 if ( propertyDefinition.getName().equals(propertyName) &&
378 (propertyDefinition.getParentUniqueId().equals(resourceUid) || !propertyDefinition.getType().equals(propertyType)) ) {
387 private Either<PropertyDefinition, StorageOperationStatus> handleProperty(String propertyId, PropertyDefinition newPropertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
389 StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(newPropertyDefinition, dataTypes);
390 if (validateAndUpdateProperty != StorageOperationStatus.OK) {
391 return Either.right(validateAndUpdateProperty);
394 return Either.left(newPropertyDefinition);
397 private StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
399 log.trace("Going to validate property type and value. {}", propertyDefinition);
401 String propertyType = propertyDefinition.getType();
402 String value = propertyDefinition.getDefaultValue();
404 ToscaPropertyType type = getType(propertyType);
408 DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
409 if (dataTypeDefinition == null) {
410 log.debug("The type {} of property cannot be found.", propertyType);
411 return StorageOperationStatus.INVALID_TYPE;
414 StorageOperationStatus status = validateAndUpdateComplexValue(propertyDefinition, propertyType, value, dataTypeDefinition, dataTypes);
419 String innerType = null;
421 Either<String, TitanOperationStatus> checkInnerType = getInnerType(type, () -> propertyDefinition.getSchema());
422 if (checkInnerType.isRight()) {
423 return StorageOperationStatus.INVALID_TYPE;
425 innerType = checkInnerType.left().value();
427 log.trace("After validating property type {}", propertyType);
429 boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
430 if (false == isValidProperty) {
431 log.info("The value {} of property from type {} is invalid", value, type);
432 return StorageOperationStatus.INVALID_VALUE;
435 PropertyValueConverter converter = type.getConverter();
437 if (isEmptyValue(value)) {
438 log.debug("Default value was not sent for property {}. Set default value to {}", propertyDefinition.getName(), EMPTY_VALUE);
439 propertyDefinition.setDefaultValue(EMPTY_VALUE);
440 } else if (false == isEmptyValue(value)) {
441 String convertedValue = converter.convert(value, innerType, dataTypes);
442 propertyDefinition.setDefaultValue(convertedValue);
444 return StorageOperationStatus.OK;
447 protected StorageOperationStatus validateAndUpdateComplexValue(IComplexDefaultValue propertyDefinition, String propertyType,
449 String value, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> dataTypes) {
451 ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
453 if (validateResult.right.booleanValue() == false) {
454 log.debug("The value {} of property from type {} is invalid", propertyType, propertyType);
455 return StorageOperationStatus.INVALID_VALUE;
458 JsonElement jsonElement = validateResult.left;
460 log.trace("Going to update value in property definition {} {}" , propertyDefinition.getName() , jsonElement);
462 updateValue(propertyDefinition, jsonElement);
464 return StorageOperationStatus.OK;
467 protected void updateValue(IComplexDefaultValue propertyDefinition, JsonElement jsonElement) {
469 propertyDefinition.setDefaultValue(getValueFromJsonElement(jsonElement));
473 protected String getValueFromJsonElement(JsonElement jsonElement) {
476 if (jsonElement == null || jsonElement.isJsonNull()) {
479 if (jsonElement.toString().isEmpty()) {
482 value = jsonElement.toString();
489 protected Either<String, TitanOperationStatus> getInnerType(ToscaPropertyType type, Supplier<SchemaDefinition> schemeGen) {
490 String innerType = null;
491 if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
493 SchemaDefinition def = schemeGen.get();
495 log.debug("Schema doesn't exists for property of type {}", type);
496 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
498 PropertyDataDefinition propDef = def.getProperty();
499 if (propDef == null) {
500 log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
501 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
503 innerType = propDef.getType();
505 return Either.left(innerType);
507 protected boolean isValidValue(ToscaPropertyType type, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
508 if (isEmptyValue(value)) {
512 PropertyTypeValidator validator = type.getValidator();
514 boolean isValid = validator.isValid(value, innerType, dataTypes);
515 if (true == isValid) {
523 public boolean isEmptyValue(String value) {