Catalog alignment
[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.collections.MapUtils;
27 import org.apache.commons.lang3.tuple.ImmutablePair;
28 import org.openecomp.sdc.be.components.impl.exceptions.BusinessLogicException;
29 import org.openecomp.sdc.be.config.BeEcompErrorManager;
30 import org.openecomp.sdc.be.dao.api.ActionStatus;
31 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
32 import org.openecomp.sdc.be.datatypes.elements.ListDataDefinition;
33 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
34 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
35 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
36 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
37 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
38 import org.openecomp.sdc.be.impl.WebAppContextWrapper;
39 import org.openecomp.sdc.be.model.Component;
40 import org.openecomp.sdc.be.model.ComponentInstanceInterface;
41 import org.openecomp.sdc.be.model.ComponentParametersView;
42 import org.openecomp.sdc.be.model.DataTypeDefinition;
43 import org.openecomp.sdc.be.model.IComplexDefaultValue;
44 import org.openecomp.sdc.be.model.InterfaceDefinition;
45 import org.openecomp.sdc.be.model.PropertyDefinition;
46 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ArtifactsOperations;
47 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.InterfaceOperation;
48 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.ToscaOperationException;
49 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
50 import org.openecomp.sdc.be.model.operations.api.IGroupInstanceOperation;
51 import org.openecomp.sdc.be.model.operations.api.IGroupOperation;
52 import org.openecomp.sdc.be.model.operations.api.IGroupTypeOperation;
53 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
54 import org.openecomp.sdc.be.model.operations.impl.InterfaceLifecycleOperation;
55 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
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.PropertyTypeValidator;
60 import org.openecomp.sdc.be.resources.data.EntryData;
61 import org.openecomp.sdc.common.api.Constants;
62 import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
63 import org.openecomp.sdc.common.log.wrappers.Logger;
64 import org.openecomp.sdc.exception.ResponseFormat;
65 import org.springframework.beans.factory.annotation.Autowired;
66 import org.springframework.web.context.WebApplicationContext;
67
68 import javax.servlet.ServletContext;
69 import java.util.ArrayList;
70 import java.util.List;
71 import java.util.Map;
72 import java.util.Objects;
73 import java.util.Optional;
74 import java.util.function.Supplier;
75
76
77 @org.springframework.stereotype.Component("propertyBusinessLogic")
78 public class PropertyBusinessLogic extends BaseBusinessLogic {
79
80     private static final String CREATE_PROPERTY = "CreateProperty";
81
82     private static final Logger log = Logger.getLogger(PropertyBusinessLogic.class);
83
84     private static final String EMPTY_VALUE = null;
85
86     @Autowired
87     public PropertyBusinessLogic(IElementOperation elementDao,
88         IGroupOperation groupOperation,
89         IGroupInstanceOperation groupInstanceOperation,
90         IGroupTypeOperation groupTypeOperation,
91         InterfaceOperation interfaceOperation,
92         InterfaceLifecycleOperation interfaceLifecycleTypeOperation,
93         ArtifactsOperations artifactToscaOperation) {
94         super(elementDao, groupOperation, groupInstanceOperation, groupTypeOperation,
95             interfaceOperation, interfaceLifecycleTypeOperation, artifactToscaOperation);
96     }
97
98     protected static IElementOperation getElementDao(Class<IElementOperation> class1, ServletContext context) {
99         WebAppContextWrapper webApplicationContextWrapper = (WebAppContextWrapper) context.getAttribute(Constants.WEB_APPLICATION_CONTEXT_WRAPPER_ATTR);
100
101         WebApplicationContext webApplicationContext = webApplicationContextWrapper.getWebAppContext(context);
102
103         return webApplicationContext.getBean(class1);
104     }
105
106     public Map<String, DataTypeDefinition> getAllDataTypes() {
107         return getAllDataTypes(applicationDataTypeCache);
108     }
109
110     /**
111      * Create new property on component in graph
112      *
113      * @param componentId
114      * @param propertyName
115      * @param newPropertyDefinition
116      * @param userId
117      * @return either properties or response format
118      */
119
120     public Either<EntryData<String, PropertyDefinition>, ResponseFormat> addPropertyToComponent(String componentId,
121                                                                                                 String propertyName,
122                                                                                                 PropertyDefinition newPropertyDefinition,
123                                                                                                 String userId) {
124         Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
125
126         validateUserExists(userId);
127
128         Either<Component, StorageOperationStatus> serviceElement =
129                 toscaOperationFacade.getToscaElement(componentId);
130         if (serviceElement.isRight()) {
131             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
132             return result;
133         }
134         Component component = serviceElement.left().value();
135         NodeTypeEnum nodeType = component.getComponentType().getNodeType();
136         StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType );
137         if (!lockResult.equals(StorageOperationStatus.OK)) {
138             BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(), componentId);
139             log.info("Failed to lock component {}. Error - {}", componentId, lockResult);
140             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
141             return result;
142         }
143
144         try {
145             if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
146                 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
147                 return result;
148             }
149
150             List<PropertyDefinition> properties = component.getProperties();
151
152             if(CollectionUtils.isEmpty(properties)) {
153                 properties = new ArrayList<>();
154             }
155
156             if(isPropertyExistInComponent(properties, propertyName)) {
157
158                 result =
159                     Either.right(componentsUtils.getResponseFormat(ActionStatus
160                         .PROPERTY_ALREADY_EXIST, propertyName));
161                 return result;
162
163             } else {
164
165                 Map<String, DataTypeDefinition> allDataTypes = getAllDataTypes(applicationDataTypeCache);
166
167                 // validate property default values
168                 Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newPropertyDefinition, allDataTypes);
169                 if (defaultValuesValidation.isRight()) {
170                     result = Either.right(defaultValuesValidation.right().value());
171                     return result;
172                 }
173                 // convert property
174                 ToscaPropertyType type = getType(newPropertyDefinition.getType());
175                 if (type != null) {
176                     PropertyValueConverter converter = type.getConverter();
177                     // get inner type
178                     String innerType = null;
179                     if (newPropertyDefinition != null) {
180                         SchemaDefinition schema = newPropertyDefinition.getSchema();
181                         if (schema != null) {
182                             PropertyDataDefinition prop = schema.getProperty();
183                             if (prop != null) {
184                                 innerType = prop.getType();
185                             }
186                         }
187                         String convertedValue = null;
188                         if (newPropertyDefinition.getDefaultValue() != null) {
189                             convertedValue = converter.convert(
190                                 newPropertyDefinition.getDefaultValue(), innerType, allDataTypes);
191                             newPropertyDefinition.setDefaultValue(convertedValue);
192                         }
193                     }
194                 }
195                 Either<PropertyDefinition, StorageOperationStatus> addPropertyEither =
196                     toscaOperationFacade
197                         .addPropertyToComponent(propertyName, newPropertyDefinition, component);
198
199                 if (addPropertyEither.isRight()) {
200                     log.info("Failed to add new property {}. Error - {}", componentId,
201                         addPropertyEither.right().value());
202                     result = Either.right(componentsUtils.getResponseFormat(ActionStatus
203                         .GENERAL_ERROR));
204                     return result;
205                 }
206             }
207             result = Either.left(new EntryData<>(propertyName, newPropertyDefinition));
208             return result;
209         } finally {
210             commitOrRollback(result);
211             // unlock component
212             graphLockOperation.unlockComponent(componentId, nodeType);
213         }
214     }
215
216     /**
217      * Copies a list of properties to a component.
218      *
219      * @param component the component to add the copied properties
220      * @param propertiesToCopyList the properties to be copied
221      * @return the updated component with the copied properties.
222      * @throws ToscaOperationException when a problem happens during the copy operation
223      */
224     public Component copyPropertyToComponent(final Component component,
225                                              final List<PropertyDefinition> propertiesToCopyList) throws ToscaOperationException {
226         return copyPropertyToComponent(component, propertiesToCopyList, true);
227     }
228
229     /**
230      * Copies a list of properties to a component.
231      *
232      * @param component the component to add the copied properties
233      * @param propertiesToCopyList the properties to be copied
234      * @param refreshComponent refresh the component from database after update
235      * @return the component refreshed from database if refreshComponent is {@code true}, the same component reference
236      * otherwise
237      * @throws ToscaOperationException when a problem happens during the copy operation
238      */
239     public Component copyPropertyToComponent(final Component component,
240                                              final List<PropertyDefinition> propertiesToCopyList,
241                                              final boolean refreshComponent) throws ToscaOperationException {
242         if (CollectionUtils.isEmpty(propertiesToCopyList)) {
243             return component;
244         }
245
246         for (final PropertyDefinition propertyDefinition : propertiesToCopyList) {
247             copyPropertyToComponent(component, propertyDefinition);
248         }
249
250         if (refreshComponent) {
251             return toscaOperationFacade.getToscaElement(component.getUniqueId()).left().value();
252         }
253
254         return component;
255     }
256
257     /**
258      * Copies one property to a component.
259      *
260      * @param component the component to add the copied property
261      * @param propertyDefinition the property to be copied
262      * @throws ToscaOperationException when a problem happens during the copy operation
263      */
264     private void copyPropertyToComponent(final Component component,
265                                          final PropertyDefinition propertyDefinition) throws ToscaOperationException {
266         final PropertyDefinition copiedPropertyDefinition = new PropertyDefinition(propertyDefinition);
267         final String componentId = component.getUniqueId();
268         final String propertyName = copiedPropertyDefinition.getName();
269         copiedPropertyDefinition.setUniqueId(
270             UniqueIdBuilder.buildPropertyUniqueId(componentId, propertyName)
271         );
272         copiedPropertyDefinition.setParentUniqueId(componentId);
273         final Either<PropertyDefinition, StorageOperationStatus> operationResult = toscaOperationFacade
274             .addPropertyToComponent(propertyName, copiedPropertyDefinition, component);
275         if (operationResult.isRight()) {
276             final String error = String.format(
277                 "Failed to add copied property '%s' to component '%s'. Operation status: '%s'",
278                 propertyDefinition.getUniqueId(), componentId, operationResult.right().value()
279             );
280             log.error(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR, PropertyBusinessLogic.class.getName(), "catalog-be", error);
281             throw new ToscaOperationException(error, operationResult.right().value());
282         }
283     }
284
285     /**
286      * Get property of component
287      *
288      * @param componentId
289      * @param propertyId
290      * @param userId
291      * @return either properties or response format
292      */
293
294     public Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> getComponentProperty(String componentId, String propertyId, String userId) {
295
296         validateUserExists(userId);
297         // Get the resource from DB
298         Either<Component, StorageOperationStatus> status =
299             toscaOperationFacade.getToscaElement(componentId);
300         if (status.isRight()) {
301             return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
302         }
303         Component component = status.left().value();
304         List<PropertyDefinition> properties = component.getProperties();
305         if(CollectionUtils.isEmpty(properties)) {
306             return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
307         }
308
309         for(PropertyDefinition property : properties) {
310             if(property.getUniqueId().equals(propertyId)) {
311                 return Either.left(new EntryData<>(property.getName(), property));
312             }
313         }
314         return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, ""));
315     }
316
317
318     public Either<List<PropertyDefinition>, ResponseFormat> getPropertiesList(String componentId,
319                                                                               String userId) {
320         validateUserExists(userId);
321
322         // Get the resource from DB
323         ComponentParametersView filter = new ComponentParametersView(true);
324         filter.setIgnoreProperties(false);
325         Either<Component, StorageOperationStatus> status =
326             toscaOperationFacade.getToscaElement(componentId);
327         if (status.isRight()) {
328             return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
329         }
330         Component component = status.left().value();
331         List<PropertyDefinition> properties = component.getProperties();
332
333         return Either.left(properties);
334     }
335
336
337     /**
338      * delete property of component from graph
339      *
340      * @param componentId
341      * @param propertyId
342      * @param userId
343      * @return either properties or response format
344      */
345
346     public Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> deletePropertyFromComponent(String componentId, String propertyId, String userId) {
347
348         Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> result = null;
349
350         validateUserExists(userId);
351
352         // Get the resource from DB
353         Either<Component, StorageOperationStatus> getComponentRes = toscaOperationFacade.getToscaElement(componentId);
354         if (getComponentRes.isRight()) {
355             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
356             return result;
357         }
358         Component component = getComponentRes.left().value();
359         NodeTypeEnum nodeType = component.getComponentType().getNodeType();
360         StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
361         if (!lockResult.equals(StorageOperationStatus.OK)) {
362             BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(),
363                     componentId);
364             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
365             return result;
366         }
367
368         try {
369             // verify that resource is checked-out and the user is the last
370             // updater
371             if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
372                 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
373                 return result;
374             }
375
376             // verify property exist in resource
377             Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty =
378                 getComponentProperty(componentId, propertyId, userId);
379             if (statusGetProperty.isRight()) {
380                 result = Either.right(statusGetProperty.right().value());
381                 return result;
382             }
383
384             Map.Entry<String, PropertyDefinition> propertyDefinitionEntry = statusGetProperty.left().value();
385
386             // verify that the property is not used by operation
387             if (isPropertyUsedByOperation(component, propertyDefinitionEntry.getValue())) {
388                 return Either.right(componentsUtils.getResponseFormat(ActionStatus
389                     .PROPERTY_USED_BY_OPERATION));
390             }
391
392             StorageOperationStatus status =
393                 toscaOperationFacade.deletePropertyOfComponent(component, propertyDefinitionEntry.getKey());
394             if (status != StorageOperationStatus.OK) {
395                 result = Either.right(componentsUtils.getResponseFormat(componentsUtils
396                     .convertFromStorageResponse(status), component.getName()));
397                 return result;
398             }
399             result = Either.left(propertyDefinitionEntry);
400             return result;
401
402         } finally {
403             commitOrRollback(result);
404             // unlock component
405             graphLockOperation.unlockComponent(componentId, nodeType);
406         }
407     }
408
409     public boolean isPropertyUsedByOperation(Component component,
410                                              PropertyDefinition propertyDefinitionEntry) {
411
412         // Component's own interfaces
413         Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
414         if(MapUtils.isNotEmpty(interfaces)){
415           for(Map.Entry<String, InterfaceDefinition> interfaceEntry : interfaces.entrySet()) {
416             if (isPropertyExistInOperationInterface(propertyDefinitionEntry, interfaceEntry.getValue())) {
417               return true;
418             }
419           }
420         }
421
422         // Component's child's component interfaces
423         if(isPropertyUsedInCIInterfaces(component.getComponentInstancesInterfaces(), propertyDefinitionEntry)){
424             return true;
425         }
426
427         // Component's parent's component interfaces
428         Either<List<Component>, StorageOperationStatus> componentList = toscaOperationFacade.getParentComponents(component.getUniqueId());
429         if(componentList.isLeft()){
430             for (Component parentComponent : componentList.left().value()) {
431                 if(isPropertyUsedInCIInterfaces(parentComponent.getComponentInstancesInterfaces(), propertyDefinitionEntry)){
432                     return true;
433                 }
434             }
435         }
436
437         return false;
438     }
439
440     private boolean isPropertyUsedInCIInterfaces(Map<String, List<ComponentInstanceInterface>> componentInstanceInterfaces, PropertyDefinition propertyDefinitionEntry){
441         Optional<ComponentInstanceInterface> isPropertyExistInOperationInterface = Optional.empty();
442         if(MapUtils.isNotEmpty(componentInstanceInterfaces)){
443             isPropertyExistInOperationInterface = componentInstanceInterfaces.entrySet().stream()
444                     .flatMap(interfaceEntry -> interfaceEntry.getValue().stream())
445                     .filter(instanceInterface -> isPropertyExistInOperationInterface(propertyDefinitionEntry, instanceInterface))
446                     .findAny();
447         }
448         return isPropertyExistInOperationInterface.isPresent();
449     }
450
451     private boolean isPropertyExistInOperationInterface(PropertyDefinition propertyDefinition,
452                                                         InterfaceDefinition interfaceDefinition) {
453         Map<String, OperationDataDefinition> operations =
454             interfaceDefinition.getOperations();
455         for(Map.Entry<String, OperationDataDefinition> operationEntry : operations
456             .entrySet()) {
457             Optional<OperationInputDefinition> inputWithDeletedPropertyCandidate =
458                 getInputWithDeclaredProperty(propertyDefinition, operationEntry);
459
460             if(inputWithDeletedPropertyCandidate.isPresent()) {
461                 return true;
462             }
463         }
464         return false;
465     }
466
467     private Optional<OperationInputDefinition> getInputWithDeclaredProperty(PropertyDefinition propertyDefinition,
468                                                                             Map.Entry<String, OperationDataDefinition> operationEntry) {
469         ListDataDefinition<OperationInputDefinition> inputs =
470             operationEntry.getValue().getInputs();
471         List<OperationInputDefinition> operationInputsList =
472             Objects.isNull(inputs) ? null : inputs.getListToscaDataDefinition();
473
474         if(CollectionUtils.isEmpty(operationInputsList)) {
475             return Optional.empty();
476         }
477
478         return operationInputsList.stream().filter(input -> input.getInputId().equals(propertyDefinition.getUniqueId())
479                 || (input.getSourceProperty() != null && input.getSourceProperty().equals(propertyDefinition.getUniqueId()))).findAny();
480     }
481
482     /**
483      * update property
484      *
485      * @param componentId
486      * @param propertyId
487      * @param newPropertyDefinition
488      * @param userId
489      * @return either properties or response format
490      */
491
492     public Either<EntryData<String, PropertyDefinition>, ResponseFormat> updateComponentProperty(String componentId,
493                                                                         String propertyId,
494                                                                         PropertyDefinition newPropertyDefinition,
495                                                                         String userId) {
496
497         Either<EntryData<String, PropertyDefinition>, ResponseFormat> result = null;
498
499         Either<Component, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(
500                 componentId);
501         if (status.isRight()) {
502             return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
503         }
504         Component component = status.left().value();
505         NodeTypeEnum nodeType = component.getComponentType().getNodeType();
506
507         if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
508             return Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
509         }
510
511         StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
512         if (!lockResult.equals(StorageOperationStatus.OK)) {
513             BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_PROPERTY, nodeType.name().toLowerCase(),
514                     componentId);
515             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
516             return result;
517         }
518
519         try {
520             Either<Map.Entry<String, PropertyDefinition>, ResponseFormat> statusGetProperty =
521                 getComponentProperty(componentId, propertyId, userId);
522             if (statusGetProperty.isRight()) {
523                 result = Either.right(statusGetProperty.right().value());
524                 return result;
525             }
526             String propertyName = statusGetProperty.left().value().getKey();
527
528             Either<PropertyDefinition, StorageOperationStatus> either =
529                 toscaOperationFacade.updatePropertyOfComponent(component, newPropertyDefinition);
530             if (either.isRight()) {
531                 result = Either.right(componentsUtils.getResponseFormatByResource(componentsUtils.convertFromStorageResponse(either.right().value()), component.getName()));
532                 return result;
533             }
534
535             EntryData<String, PropertyDefinition> property = new EntryData<>(propertyName, either.left().value());
536             result = Either.left(property);
537             return result;
538
539         } finally {
540             commitOrRollback(result);
541             graphLockOperation.unlockComponent(componentId, nodeType);
542         }
543
544     }
545
546     /**
547      * Finds a component by id,
548      *
549      * @param componentId the component id to find
550      * @return an Optional<Component> if the component with given id was found, otherwise Optional.empty()
551      * @throws BusinessLogicException when a problem happens during the find operation
552      */
553     public Optional<Component> findComponentById(final String componentId) throws BusinessLogicException {
554         final Either<Component, StorageOperationStatus> status = toscaOperationFacade.getToscaElement(componentId);
555         if (status.isRight()) {
556             final StorageOperationStatus operationStatus = status.right().value();
557             if (operationStatus == StorageOperationStatus.NOT_FOUND) {
558                 return Optional.empty();
559             }
560             final ResponseFormat responseFormat = componentsUtils.getResponseFormat(operationStatus);
561             throw new BusinessLogicException(responseFormat);
562         }
563         return Optional.ofNullable(status.left().value());
564     }
565
566     /**
567      * Updates a component property.
568      *
569      * @param componentId the component id that owns the property
570      * @param propertyDefinition the existing property to update
571      * @return the updated property
572      * @throws BusinessLogicException if the component was not found or if there was a problem during the update
573      * operation.
574      */
575     public PropertyDefinition updateComponentProperty(final String componentId,
576                                                       final PropertyDefinition propertyDefinition)
577             throws BusinessLogicException {
578         final Component component = findComponentById(componentId).orElse(null);
579         if (component == null) {
580             throw new BusinessLogicException(
581                 componentsUtils.getResponseFormatByResource(ActionStatus.RESOURCE_NOT_FOUND, componentId));
582         }
583         final Either<PropertyDefinition, StorageOperationStatus> updateResultEither =
584             toscaOperationFacade.updatePropertyOfComponent(component, propertyDefinition);
585         if (updateResultEither.isRight()) {
586             final ResponseFormat responseFormat = componentsUtils.getResponseFormatByResource(
587                 componentsUtils.convertFromStorageResponse(updateResultEither.right().value()), component.getName()
588             );
589             throw new BusinessLogicException(responseFormat);
590         }
591
592         return updateResultEither.left().value();
593     }
594
595     private boolean isPropertyExistInComponent(List<PropertyDefinition> properties, String propertyName) {
596         if(CollectionUtils.isEmpty(properties)) {
597             return false;
598         }
599
600         Optional<PropertyDefinition> propertyCandidate =
601                 properties.stream().filter(property -> property.getName().equals(propertyName))
602                         .findAny();
603
604         return propertyCandidate.isPresent();
605     }
606
607     private StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
608
609         log.trace("Going to validate property type and value. {}", propertyDefinition);
610
611         String propertyType = propertyDefinition.getType();
612         String value = propertyDefinition.getDefaultValue();
613
614         ToscaPropertyType type = getType(propertyType);
615
616         if (type == null) {
617             DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
618             if (dataTypeDefinition == null) {
619                 log.debug("The type {} of property cannot be found.", propertyType);
620                 return StorageOperationStatus.INVALID_TYPE;
621             }
622             return validateAndUpdateComplexValue(propertyDefinition, propertyType, value, dataTypeDefinition, dataTypes);
623         }
624         String innerType;
625
626         Either<String, JanusGraphOperationStatus> checkInnerType = getInnerType(type, propertyDefinition::getSchema);
627         if (checkInnerType.isRight()) {
628             return StorageOperationStatus.INVALID_TYPE;
629         }
630         innerType = checkInnerType.left().value();
631
632         log.trace("After validating property type {}", propertyType);
633
634         boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
635         if (!isValidProperty) {
636             log.info("The value {} of property from type {} is invalid", value, type);
637             return StorageOperationStatus.INVALID_VALUE;
638         }
639
640         PropertyValueConverter converter = type.getConverter();
641
642         if (isEmptyValue(value)) {
643             log.debug("Default value was not sent for property {}. Set default value to {}", propertyDefinition.getName(), EMPTY_VALUE);
644             propertyDefinition.setDefaultValue(EMPTY_VALUE);
645         } else if (!isEmptyValue(value)) {
646             String convertedValue = converter.convert(value, innerType, dataTypes);
647             propertyDefinition.setDefaultValue(convertedValue);
648         }
649         return StorageOperationStatus.OK;
650     }
651
652     private StorageOperationStatus validateAndUpdateComplexValue(IComplexDefaultValue propertyDefinition, String propertyType,
653                                                                  String value, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> dataTypes) {
654
655         ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
656
657         if (validateResult.right) {
658             log.debug("The value {} of property from type {} is invalid", propertyType, propertyType);
659             return StorageOperationStatus.INVALID_VALUE;
660         }
661
662         JsonElement jsonElement = validateResult.left;
663
664         log.trace("Going to update value in property definition {} {}" , propertyDefinition.getName() , jsonElement);
665
666         updateValue(propertyDefinition, jsonElement);
667
668         return StorageOperationStatus.OK;
669     }
670
671     private void updateValue(IComplexDefaultValue propertyDefinition, JsonElement jsonElement) {
672
673         propertyDefinition.setDefaultValue(getValueFromJsonElement(jsonElement));
674
675     }
676
677     @Override
678     protected String getValueFromJsonElement(JsonElement jsonElement) {
679         if (jsonElement == null || jsonElement.isJsonNull()) {
680             return EMPTY_VALUE;
681         }
682         if(jsonElement.toString().isEmpty()){
683             return "";
684         }
685         return jsonElement.toString();
686     }
687
688     private Either<String, JanusGraphOperationStatus> getInnerType(ToscaPropertyType type, Supplier<SchemaDefinition> schemeGen) {
689         String innerType = null;
690         if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
691
692             SchemaDefinition def = schemeGen.get();
693             if (def == null) {
694                 log.debug("Schema doesn't exists for property of type {}", type);
695                 return Either.right(JanusGraphOperationStatus.ILLEGAL_ARGUMENT);
696             }
697             PropertyDataDefinition propDef = def.getProperty();
698             if (propDef == null) {
699                 log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
700                 return Either.right(JanusGraphOperationStatus.ILLEGAL_ARGUMENT);
701             }
702             innerType = propDef.getType();
703         }
704         return Either.left(innerType);
705     }
706
707     @Override
708     protected boolean isValidValue(ToscaPropertyType type, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
709         if (isEmptyValue(value)) {
710             return true;
711         }
712         PropertyTypeValidator validator = type.getValidator();
713         return validator.isValid(value, innerType, dataTypes);
714     }
715
716     @Override
717     public boolean isEmptyValue(String value) {
718         return value == null;
719     }
720 }