Sync Integ to Master
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / PropertyBusinessLogic.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
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
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
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=========================================================
19  */
20
21 package org.openecomp.sdc.be.components.impl;
22
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;
53
54 import javax.servlet.ServletContext;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Map;
58 import java.util.Map.Entry;
59 import java.util.function.Supplier;
60
61 @Component("propertyBusinessLogic")
62 public class PropertyBusinessLogic extends BaseBusinessLogic {
63
64     private static final String CREATE_PROPERTY = "CreateProperty";
65
66     private static final Logger log = LoggerFactory.getLogger(PropertyBusinessLogic.class);
67
68     private static final String EMPTY_VALUE = null;
69
70     private DataTypeValidatorConverter dataTypeValidatorConverter = DataTypeValidatorConverter.getInstance();
71
72     protected static IElementOperation getElementDao(Class<IElementOperation> class1, ServletContext context) {
73         WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
74
75         WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
76
77         return webApplicationContext.getBean(class1);
78     }
79
80     public Either<Map<String, DataTypeDefinition>, ResponseFormat> getAllDataTypes() {
81         Either<Map<String, DataTypeDefinition>, ResponseFormat> eitherAllDataTypes = getAllDataTypes(applicationDataTypeCache);
82         return eitherAllDataTypes;
83     }
84
85     /**
86      * Create new property on resource in graph
87      *
88      * @param resourceId
89      * @param propertyName
90      * @param newPropertyDefinition
91      * @param userId
92      * @return Either<PropertyDefinition, ActionStatus>
93      */
94     public Either<EntryData<String, PropertyDefinition>, ResponseFormat> createProperty(String resourceId, String propertyName, PropertyDefinition newPropertyDefinition, String userId) {
95
96         Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
97
98         Either<User, ResponseFormat> resp = validateUserExists(userId, "create Property", false);
99         if (resp.isRight()) {
100             result = Either.right(resp.right().value());
101             return result;
102         }
103
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));
109             return result;
110         }
111
112         try {
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, ""));
117                 return result;
118             }
119             Resource resource = status.left().value();
120
121             // verify that resource is checked-out and the user is the last
122             // updater
123             if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
124                 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
125                 return result;
126             }
127
128             // verify property not exist in resource
129             List<PropertyDefinition> resourceProperties = resource.getProperties();
130
131             if (resourceProperties != null) {
132                 if (isPropertyExist(resourceProperties, resourceId, propertyName, newPropertyDefinition.getType())) {
133                     result = Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_ALREADY_EXIST, propertyName));
134                     return result;
135                 }
136             }
137
138             Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
139             if (allDataTypes.isRight()) {
140                 result = Either.right(allDataTypes.right().value());
141                 return result;
142             }
143
144             Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
145
146             // validate property default values
147             Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
148             if (defaultValuesValidation.isRight()) {
149                 result = Either.right(defaultValuesValidation.right().value());
150                 return result;
151             }
152             // convert property
153             ToscaPropertyType type = getType(newPropertyDefinition.getType());
154             if (type != null) {
155                 PropertyValueConverter converter = type.getConverter();
156                 // get inner type
157                 String innerType = null;
158                 if (newPropertyDefinition != null) {
159                     SchemaDefinition schema = newPropertyDefinition.getSchema();
160                     if (schema != null) {
161                         PropertyDataDefinition prop = schema.getProperty();
162                         if (prop != null) {
163                             innerType = prop.getType();
164                         }
165                     }
166                     String convertedValue = null;
167                     if (newPropertyDefinition.getDefaultValue() != null) {
168                         convertedValue = converter.convert(newPropertyDefinition.getDefaultValue(), innerType, allDataTypes.left().value());
169                         newPropertyDefinition.setDefaultValue(convertedValue);
170                     }
171                 }
172             }
173
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()));
180                 return result;
181             }
182
183             PropertyDefinition createdPropertyDefinition = either.left().value();
184             EntryData<String, PropertyDefinition> property = new EntryData<String, PropertyDefinition>(propertyName, createdPropertyDefinition);
185             result = Either.left(property);
186             return result;
187
188         } finally {
189             commitOrRollback(result);
190             // unlock component
191             graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
192         }
193
194     }
195
196     /**
197      * Get property of resource
198      *
199      * @param resourceId
200      * @param propertyId
201      * @param userId
202      * @return
203      */
204     public Either<Entry<String, PropertyDefinition>, ResponseFormat> getProperty(String resourceId, String propertyId, String userId) {
205
206         Either<User, ResponseFormat> resp = validateUserExists(userId, "create Component Instance", false);
207         if (resp.isRight()) {
208             return Either.right(resp.right().value());
209         }
210
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, ""));
215         }
216         Resource resource = status.left().value();
217
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, ""));
222         }
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());
228             }
229         }
230         return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
231     }
232
233     /**
234      * delete property of resource from graph
235      *
236      * @param resourceId
237      * @param propertyId
238      * @param userId
239      * @return
240      */
241     public Either<Entry<String, PropertyDefinition>, ResponseFormat> deleteProperty(String resourceId, String propertyId, String userId) {
242
243         Either<Entry<String, PropertyDefinition>, ResponseFormat> result = null;
244
245         Either<User, ResponseFormat> resp = validateUserExists(userId, "delete Property", false);
246         if (resp.isRight()) {
247             return Either.right(resp.right().value());
248         }
249
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));
254             return result;
255         }
256
257         try {
258
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, ""));
263                 return result;
264             }
265             Resource resource = getResourceRes.left().value();
266
267             // verify that resource is checked-out and the user is the last
268             // updater
269             if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
270                 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
271                 return result;
272             }
273
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());
278                 return result;
279             }
280
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()));
284                 return result;
285             }
286             result = Either.left(statusGetProperty.left().value());
287             return result;
288
289         } finally {
290             commitOrRollback(result);
291             // unlock component
292             graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
293         }
294     }
295
296     /**
297      * update property
298      *
299      * @param resourceId
300      * @param propertyId
301      * @param newPropertyDefinition
302      * @param userId
303      * @return
304      */
305     public Either<EntryData<String, PropertyDefinition>, ResponseFormat> updateProperty(String resourceId, String propertyId, PropertyDefinition newPropertyDefinition, String userId) {
306
307         Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
308
309         Either<Resource, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(resourceId);
310         if (status.isRight()) {
311             return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
312         }
313         Resource resource = status.left().value();
314
315         if (!ComponentValidationUtils.canWorkOnResource(resource, userId)) {
316             return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
317         }
318
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));
323             return result;
324         }
325
326         try {
327             Either<Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty = getProperty(resourceId, propertyId, userId);
328             if (statusGetProperty.isRight()) {
329                 result = Either.right(statusGetProperty.right().value());
330                 return result;
331             }
332             String propertyName = statusGetProperty.left().value().getKey();
333
334             Either<Map<String, DataTypeDefinition>, ResponseFormat> allDataTypes = getAllDataTypes(applicationDataTypeCache);
335             if (allDataTypes.isRight()) {
336                 result = Either.right(allDataTypes.right().value());
337                 return result;
338             }
339             Map<String, DataTypeDefinition> dataTypes = allDataTypes.left().value();
340
341             Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, dataTypes);
342             if (defaultValuesValidation.isRight()) {
343                 result = Either.right(defaultValuesValidation.right().value());
344                 return result;
345             }
346
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()));
351                 return result;
352             }
353
354
355             either = toscaOperationFacade.updatePropertyOfResource(resource, newPropertyDefinition);
356             if (either.isRight()) {
357                 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), resource.getName()));
358                 return result;
359             }
360
361             EntryData<String, PropertyDefinition> property = new EntryData<String, PropertyDefinition>(propertyName, either.left().value());
362             result = Either.left(property);
363             return result;
364
365         } finally {
366             commitOrRollback(result);
367             graphLockOperation.unlockComponent(resourceId, NodeTypeEnum.Resource);
368         }
369
370     }
371
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) {
376
377                 if ( propertyDefinition.getName().equals(propertyName) &&
378                         (propertyDefinition.getParentUniqueId().equals(resourceUid) || !propertyDefinition.getType().equals(propertyType)) ) {
379                     result = true;
380                     break;
381                 }
382             }
383         }
384         return result;
385     }
386
387     private Either<PropertyDefinition, StorageOperationStatus> handleProperty(String propertyId, PropertyDefinition newPropertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
388
389         StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(newPropertyDefinition, dataTypes);
390         if (validateAndUpdateProperty != StorageOperationStatus.OK) {
391             return Either.right(validateAndUpdateProperty);
392         }
393
394         return Either.left(newPropertyDefinition);
395     }
396
397     private StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
398
399         log.trace("Going to validate property type and value. {}", propertyDefinition);
400
401         String propertyType = propertyDefinition.getType();
402         String value = propertyDefinition.getDefaultValue();
403
404         ToscaPropertyType type = getType(propertyType);
405
406         if (type == null) {
407
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;
412             }
413
414             StorageOperationStatus status = validateAndUpdateComplexValue(propertyDefinition, propertyType, value, dataTypeDefinition, dataTypes);
415
416             return status;
417
418         }
419         String innerType = null;
420
421         Either<String, TitanOperationStatus> checkInnerType = getInnerType(type, () -> propertyDefinition.getSchema());
422         if (checkInnerType.isRight()) {
423             return StorageOperationStatus.INVALID_TYPE;
424         }
425         innerType = checkInnerType.left().value();
426
427         log.trace("After validating property type {}", propertyType);
428
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;
433         }
434
435         PropertyValueConverter converter = type.getConverter();
436
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);
443         }
444         return StorageOperationStatus.OK;
445     }
446
447     protected StorageOperationStatus validateAndUpdateComplexValue(IComplexDefaultValue propertyDefinition, String propertyType,
448
449             String value, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> dataTypes) {
450
451         ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
452
453         if (validateResult.right.booleanValue() == false) {
454             log.debug("The value {} of property from type {} is invalid", propertyType, propertyType);
455             return StorageOperationStatus.INVALID_VALUE;
456         }
457
458         JsonElement jsonElement = validateResult.left;
459
460         log.trace("Going to update value in property definition {} {}" , propertyDefinition.getName() , jsonElement);
461
462         updateValue(propertyDefinition, jsonElement);
463
464         return StorageOperationStatus.OK;
465     }
466
467     protected void updateValue(IComplexDefaultValue propertyDefinition, JsonElement jsonElement) {
468
469         propertyDefinition.setDefaultValue(getValueFromJsonElement(jsonElement));
470
471     }
472
473     protected String getValueFromJsonElement(JsonElement jsonElement) {
474         String value = null;
475
476         if (jsonElement == null || jsonElement.isJsonNull()) {
477             value = EMPTY_VALUE;
478         } else {
479             if (jsonElement.toString().isEmpty()) {
480                 value = "";
481             } else {
482                 value = jsonElement.toString();
483             }
484         }
485
486         return value;
487     }
488
489     protected Either<String, TitanOperationStatus> getInnerType(ToscaPropertyType type, Supplier<SchemaDefinition> schemeGen) {
490         String innerType = null;
491         if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
492
493             SchemaDefinition def = schemeGen.get();
494             if (def == null) {
495                 log.debug("Schema doesn't exists for property of type {}", type);
496                 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
497             }
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);
502             }
503             innerType = propDef.getType();
504         }
505         return Either.left(innerType);
506     }
507     protected boolean isValidValue(ToscaPropertyType type, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
508         if (isEmptyValue(value)) {
509             return true;
510         }
511
512         PropertyTypeValidator validator = type.getValidator();
513
514         boolean isValid = validator.isValid(value, innerType, dataTypes);
515         if (true == isValid) {
516             return true;
517         } else {
518             return false;
519         }
520
521     }
522
523     public boolean isEmptyValue(String value) {
524         if (value == null) {
525             return true;
526         }
527         return false;
528     }
529 }