Fix bug 'Pattern constraint validation failure'
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / operations / impl / PropertyOperation.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 package org.openecomp.sdc.be.model.operations.impl;
21
22 import static org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR;
23
24 import com.fasterxml.jackson.core.ObjectCodec;
25 import com.fasterxml.jackson.databind.DeserializationContext;
26 import com.fasterxml.jackson.databind.JsonNode;
27 import com.fasterxml.jackson.databind.node.ArrayNode;
28 import com.google.common.collect.Maps;
29 import com.google.gson.JsonArray;
30 import com.google.gson.JsonDeserializationContext;
31 import com.google.gson.JsonDeserializer;
32 import com.google.gson.JsonElement;
33 import com.google.gson.JsonObject;
34 import com.google.gson.JsonParseException;
35 import com.google.gson.JsonParser;
36 import com.google.gson.JsonPrimitive;
37 import com.google.gson.JsonSerializationContext;
38 import com.google.gson.JsonSerializer;
39 import com.google.gson.JsonSyntaxException;
40 import fj.data.Either;
41 import java.io.IOException;
42 import java.lang.reflect.InvocationTargetException;
43 import java.lang.reflect.Type;
44 import java.util.ArrayList;
45 import java.util.Collection;
46 import java.util.Collections;
47 import java.util.HashMap;
48 import java.util.Iterator;
49 import java.util.List;
50 import java.util.Map;
51 import java.util.Map.Entry;
52 import java.util.Set;
53 import java.util.function.Consumer;
54 import java.util.regex.Matcher;
55 import java.util.regex.Pattern;
56 import java.util.stream.Collectors;
57 import org.apache.commons.collections.CollectionUtils;
58 import org.apache.commons.collections.MapUtils;
59 import org.apache.commons.lang3.StringUtils;
60 import org.apache.commons.lang3.tuple.ImmutablePair;
61 import org.apache.tinkerpop.gremlin.structure.Edge;
62 import org.apache.tinkerpop.gremlin.structure.Vertex;
63 import org.apache.tinkerpop.gremlin.structure.VertexProperty;
64 import org.janusgraph.core.JanusGraph;
65 import org.janusgraph.core.JanusGraphVertex;
66 import org.janusgraph.core.JanusGraphVertexProperty;
67 import org.openecomp.sdc.be.config.BeEcompErrorManager;
68 import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
69 import org.openecomp.sdc.be.dao.graph.GraphElementFactory;
70 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
71 import org.openecomp.sdc.be.dao.graph.datatype.GraphElementTypeEnum;
72 import org.openecomp.sdc.be.dao.graph.datatype.GraphNode;
73 import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
74 import org.openecomp.sdc.be.dao.janusgraph.HealingJanusGraphGenericDao;
75 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao;
76 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
77 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
78 import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
79 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
80 import org.openecomp.sdc.be.datatypes.elements.PropertyRule;
81 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
82 import org.openecomp.sdc.be.datatypes.enums.ConstraintType;
83 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
84 import org.openecomp.sdc.be.model.Component;
85 import org.openecomp.sdc.be.model.ComponentInstanceProperty;
86 import org.openecomp.sdc.be.model.DataTypeDefinition;
87 import org.openecomp.sdc.be.model.IComplexDefaultValue;
88 import org.openecomp.sdc.be.model.PropertyConstraint;
89 import org.openecomp.sdc.be.model.PropertyDefinition;
90 import org.openecomp.sdc.be.model.operations.api.DerivedFromOperation;
91 import org.openecomp.sdc.be.model.operations.api.IPropertyOperation;
92 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
93 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
94 import org.openecomp.sdc.be.model.tosca.constraints.EqualConstraint;
95 import org.openecomp.sdc.be.model.tosca.constraints.GreaterOrEqualConstraint;
96 import org.openecomp.sdc.be.model.tosca.constraints.GreaterThanConstraint;
97 import org.openecomp.sdc.be.model.tosca.constraints.InRangeConstraint;
98 import org.openecomp.sdc.be.model.tosca.constraints.LengthConstraint;
99 import org.openecomp.sdc.be.model.tosca.constraints.LessOrEqualConstraint;
100 import org.openecomp.sdc.be.model.tosca.constraints.LessThanConstraint;
101 import org.openecomp.sdc.be.model.tosca.constraints.MaxLengthConstraint;
102 import org.openecomp.sdc.be.model.tosca.constraints.MinLengthConstraint;
103 import org.openecomp.sdc.be.model.tosca.constraints.PatternConstraint;
104 import org.openecomp.sdc.be.model.tosca.constraints.ValidValuesConstraint;
105 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
106 import org.openecomp.sdc.be.model.validation.ToscaFunctionValidator;
107 import org.openecomp.sdc.be.resources.data.ComponentInstanceData;
108 import org.openecomp.sdc.be.resources.data.DataTypeData;
109 import org.openecomp.sdc.be.resources.data.ModelData;
110 import org.openecomp.sdc.be.resources.data.PropertyData;
111 import org.openecomp.sdc.be.resources.data.PropertyValueData;
112 import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
113 import org.openecomp.sdc.be.resources.data.UniqueIdData;
114 import org.openecomp.sdc.common.log.wrappers.Logger;
115 import org.springframework.beans.factory.annotation.Autowired;
116
117 @org.springframework.stereotype.Component("property-operation")
118 public class PropertyOperation extends AbstractOperation implements IPropertyOperation {
119
120     private static final String AFTER_RETRIEVING_DERIVED_FROM_NODE_OF_STATUS_IS = "After retrieving DERIVED_FROM node of {}. status is {}";
121     private static final String FAILED_TO_FETCH_PROPERTIES_OF_DATA_TYPE = "Failed to fetch properties of data type {}";
122     private static final String DATA_TYPE_CANNOT_BE_FOUND_IN_GRAPH_STATUS_IS = "Data type {} cannot be found in graph. status is {}";
123     private static final String GOING_TO_EXECUTE_COMMIT_ON_GRAPH = "Going to execute commit on graph.";
124     private static final String GOING_TO_EXECUTE_ROLLBACK_ON_GRAPH = "Going to execute rollback on graph.";
125     private static final String FAILED_TO_ASSOCIATE_RESOURCE_TO_PROPERTY_IN_GRAPH_STATUS_IS = "Failed to associate resource {} to property {} in graph. status is {}";
126     private static final String AFTER_ADDING_PROPERTY_TO_GRAPH = "After adding property to graph {}";
127     private static final String BEFORE_ADDING_PROPERTY_TO_GRAPH = "Before adding property to graph {}";
128     private static final String THE_VALUE_OF_PROPERTY_FROM_TYPE_IS_INVALID = "The value {} of property from type {} is invalid";
129     private static final String PROPERTY = "Property";
130     private static final String UPDATE_DATA_TYPE = "UpdateDataType";
131     private static final Logger log = Logger.getLogger(PropertyOperation.class.getName());
132     private final DerivedFromOperation derivedFromOperation;
133     private ToscaFunctionValidator toscaFunctionValidator;
134     private DataTypeOperation dataTypeOperation;
135
136     @Autowired
137     public PropertyOperation(final HealingJanusGraphGenericDao janusGraphGenericDao, final DerivedFromOperation derivedFromOperation) {
138         this.janusGraphGenericDao = janusGraphGenericDao;
139         this.derivedFromOperation = derivedFromOperation;
140     }
141
142     @Autowired
143     public void setToscaFunctionValidator(final ToscaFunctionValidator toscaFunctionValidator) {
144         this.toscaFunctionValidator = toscaFunctionValidator;
145     }
146
147     //circular dependency DataTypeOperation->ModelOperation->ModelElementOperation->PropertyOperation
148     @Autowired
149     public void setDataTypeOperation(DataTypeOperation dataTypeOperation) {
150         this.dataTypeOperation = dataTypeOperation;
151     }
152
153     public PropertyDefinition convertPropertyDataToPropertyDefinition(PropertyData propertyDataResult, String propertyName, String resourceId) {
154         log.debug("The object returned after create property is {}", propertyDataResult);
155         PropertyDefinition propertyDefResult = new PropertyDefinition(propertyDataResult.getPropertyDataDefinition());
156         propertyDefResult.setConstraints(convertConstraints(propertyDataResult.getConstraints()));
157         propertyDefResult.setName(propertyName);
158         return propertyDefResult;
159     }
160
161     public Either<PropertyData, StorageOperationStatus> addProperty(String propertyName, PropertyDefinition propertyDefinition, String resourceId) {
162         Either<PropertyData, JanusGraphOperationStatus> either = addPropertyToGraph(propertyName, propertyDefinition, resourceId);
163         if (either.isRight()) {
164             StorageOperationStatus storageStatus = DaoStatusConverter.convertJanusGraphStatusToStorageStatus(either.right().value());
165             return Either.right(storageStatus);
166         }
167         return Either.left(either.left().value());
168     }
169
170     /**
171      * @param propertyDefinition
172      * @return
173      */
174     @Override
175     public StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
176         log.trace("Going to validate property type and value. {}", propertyDefinition);
177         String propertyType = propertyDefinition.getType();
178         String value = propertyDefinition.getDefaultValue();
179         ToscaPropertyType type = getType(propertyType);
180         if (type == null) {
181             DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
182             if (dataTypeDefinition == null) {
183                 log.debug("The type {} of property cannot be found.", propertyType);
184                 return StorageOperationStatus.INVALID_TYPE;
185             }
186             return validateAndUpdateComplexValue(propertyDefinition, propertyType, value, dataTypeDefinition, dataTypes);
187         }
188         String innerType = null;
189         Either<String, JanusGraphOperationStatus> checkInnerType = getInnerType(type, propertyDefinition::getSchema);
190         if (checkInnerType.isRight()) {
191             return StorageOperationStatus.INVALID_TYPE;
192         }
193         innerType = checkInnerType.left().value();
194         log.trace("After validating property type {}", propertyType);
195         boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
196         if (!isValidProperty) {
197             log.info(THE_VALUE_OF_PROPERTY_FROM_TYPE_IS_INVALID, value, type);
198             return StorageOperationStatus.INVALID_VALUE;
199         }
200         PropertyValueConverter converter = type.getConverter();
201         if (isEmptyValue(value)) {
202             log.debug("Default value was not sent for property {}. Set default value to {}", propertyDefinition.getName(), EMPTY_VALUE);
203             propertyDefinition.setDefaultValue(EMPTY_VALUE);
204         } else if (!isEmptyValue(value)) {
205             String convertedValue = converter.convert(value, innerType, dataTypes);
206             propertyDefinition.setDefaultValue(convertedValue);
207         }
208         return StorageOperationStatus.OK;
209     }
210
211     public Either<PropertyData, JanusGraphOperationStatus> addPropertyToGraph(String propertyName, PropertyDefinition propertyDefinition,
212                                                                               String resourceId) {
213         ResourceMetadataData resourceData = new ResourceMetadataData();
214         resourceData.getMetadataDataDefinition().setUniqueId(resourceId);
215         List<PropertyConstraint> constraints = propertyDefinition.getConstraints();
216         propertyDefinition.setUniqueId(UniqueIdBuilder.buildComponentPropertyUniqueId(resourceId, propertyName));
217         PropertyData propertyData = new PropertyData(propertyDefinition, convertConstraintsToString(constraints));
218         log.debug(BEFORE_ADDING_PROPERTY_TO_GRAPH, propertyData);
219         Either<PropertyData, JanusGraphOperationStatus> createNodeResult = janusGraphGenericDao.createNode(propertyData, PropertyData.class);
220         log.debug(AFTER_ADDING_PROPERTY_TO_GRAPH, propertyData);
221         if (createNodeResult.isRight()) {
222             JanusGraphOperationStatus operationStatus = createNodeResult.right().value();
223             log.error("Failed to add property {} to graph. status is {}", propertyName, operationStatus);
224             return Either.right(operationStatus);
225         }
226         Map<String, Object> props = new HashMap<>();
227         props.put(GraphPropertiesDictionary.NAME.getProperty(), propertyName);
228         Either<GraphRelation, JanusGraphOperationStatus> createRelResult = janusGraphGenericDao
229             .createRelation(resourceData, propertyData, GraphEdgeLabels.PROPERTY, props);
230         if (createRelResult.isRight()) {
231             JanusGraphOperationStatus operationStatus = createNodeResult.right().value();
232             log.error(FAILED_TO_ASSOCIATE_RESOURCE_TO_PROPERTY_IN_GRAPH_STATUS_IS, resourceId, propertyName, operationStatus);
233             return Either.right(operationStatus);
234         }
235         return Either.left(createNodeResult.left().value());
236     }
237
238     public JanusGraphOperationStatus addPropertyToGraphByVertex(JanusGraphVertex metadataVertex, String propertyName,
239                                                                 PropertyDefinition propertyDefinition, String resourceId) {
240         List<PropertyConstraint> constraints = propertyDefinition.getConstraints();
241         propertyDefinition.setUniqueId(UniqueIdBuilder.buildComponentPropertyUniqueId(resourceId, propertyName));
242         PropertyData propertyData = new PropertyData(propertyDefinition, convertConstraintsToString(constraints));
243         log.debug(BEFORE_ADDING_PROPERTY_TO_GRAPH, propertyData);
244         Either<JanusGraphVertex, JanusGraphOperationStatus> createNodeResult = janusGraphGenericDao.createNode(propertyData);
245         log.debug(AFTER_ADDING_PROPERTY_TO_GRAPH, propertyData);
246         if (createNodeResult.isRight()) {
247             JanusGraphOperationStatus operationStatus = createNodeResult.right().value();
248             log.error("Failed to add property {} to graph. status is ", propertyName, operationStatus);
249             return operationStatus;
250         }
251         Map<String, Object> props = new HashMap<>();
252         props.put(GraphPropertiesDictionary.NAME.getProperty(), propertyName);
253         JanusGraphVertex propertyVertex = createNodeResult.left().value();
254         JanusGraphOperationStatus createRelResult = janusGraphGenericDao.createEdge(metadataVertex, propertyVertex, GraphEdgeLabels.PROPERTY, props);
255         if (!createRelResult.equals(JanusGraphOperationStatus.OK)) {
256             log.error(FAILED_TO_ASSOCIATE_RESOURCE_TO_PROPERTY_IN_GRAPH_STATUS_IS, resourceId, propertyName, createRelResult);
257             return createRelResult;
258         }
259         return createRelResult;
260     }
261
262     public JanusGraphGenericDao getJanusGraphGenericDao() {
263         return janusGraphGenericDao;
264     }
265
266     /**
267      * FOR TEST ONLY
268      *
269      * @param janusGraphGenericDao
270      */
271     public void setJanusGraphGenericDao(HealingJanusGraphGenericDao janusGraphGenericDao) {
272         this.janusGraphGenericDao = janusGraphGenericDao;
273     }
274
275     public Either<PropertyData, JanusGraphOperationStatus> deletePropertyFromGraph(String propertyId) {
276         log.debug("Before deleting property from graph {}", propertyId);
277         return janusGraphGenericDao.deleteNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Property), propertyId, PropertyData.class);
278     }
279
280     public Either<PropertyData, StorageOperationStatus> updateProperty(String propertyId, PropertyDefinition newPropertyDefinition,
281                                                                        Map<String, DataTypeDefinition> dataTypes) {
282         StorageOperationStatus validateAndUpdateProperty = validateAndUpdateProperty(newPropertyDefinition, dataTypes);
283         if (validateAndUpdateProperty != StorageOperationStatus.OK) {
284             return Either.right(validateAndUpdateProperty);
285         }
286         Either<PropertyData, JanusGraphOperationStatus> either = updatePropertyFromGraph(propertyId, newPropertyDefinition);
287         if (either.isRight()) {
288             StorageOperationStatus storageStatus = DaoStatusConverter.convertJanusGraphStatusToStorageStatus(either.right().value());
289             return Either.right(storageStatus);
290         }
291         return Either.left(either.left().value());
292     }
293
294     public Either<PropertyData, JanusGraphOperationStatus> updatePropertyFromGraph(String propertyId, PropertyDefinition propertyDefinition) {
295         if (log.isDebugEnabled()) {
296             log.debug("Before updating property on graph {}", propertyId);
297         }
298         // get the original property data
299         Either<PropertyData, JanusGraphOperationStatus> statusProperty = janusGraphGenericDao
300             .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Property), propertyId, PropertyData.class);
301         if (statusProperty.isRight()) {
302             log.debug("Problem while get property with id {}. Reason - {}", propertyId, statusProperty.right().value().name());
303             return Either.right(statusProperty.right().value());
304         }
305         PropertyData orgPropertyData = statusProperty.left().value();
306         PropertyDataDefinition orgPropertyDataDefinition = orgPropertyData.getPropertyDataDefinition();
307         // create new property data to update
308         PropertyData newPropertyData = new PropertyData();
309         newPropertyData.setPropertyDataDefinition(propertyDefinition);
310         PropertyDataDefinition newPropertyDataDefinition = newPropertyData.getPropertyDataDefinition();
311         // update the original property data with new values
312         if (orgPropertyDataDefinition.getDefaultValue() == null) {
313             orgPropertyDataDefinition.setDefaultValue(newPropertyDataDefinition.getDefaultValue());
314         } else {
315             if (!orgPropertyDataDefinition.getDefaultValue().equals(newPropertyDataDefinition.getDefaultValue())) {
316                 orgPropertyDataDefinition.setDefaultValue(newPropertyDataDefinition.getDefaultValue());
317             }
318         }
319         if (orgPropertyDataDefinition.getDescription() == null) {
320             orgPropertyDataDefinition.setDescription(newPropertyDataDefinition.getDescription());
321         } else {
322             if (!orgPropertyDataDefinition.getDescription().equals(newPropertyDataDefinition.getDescription())) {
323                 orgPropertyDataDefinition.setDescription(newPropertyDataDefinition.getDescription());
324             }
325         }
326         if (!orgPropertyDataDefinition.getType().equals(newPropertyDataDefinition.getType())) {
327             orgPropertyDataDefinition.setType(newPropertyDataDefinition.getType());
328         }
329         if (newPropertyData.getConstraints() != null) {
330             orgPropertyData.setConstraints(newPropertyData.getConstraints());
331         }
332         orgPropertyDataDefinition.setSchema(newPropertyDataDefinition.getSchema());
333         return janusGraphGenericDao.updateNode(orgPropertyData, PropertyData.class);
334     }
335
336     public Either<PropertyData, JanusGraphOperationStatus> addPropertyToNodeType(String propertyName, PropertyDefinition propertyDefinition,
337                                                                                  NodeTypeEnum nodeType, String uniqueId) {
338         return addPropertyToNodeType(propertyName, propertyDefinition, nodeType, uniqueId, true);
339     }
340
341     public Either<PropertyData, JanusGraphOperationStatus> addPropertyToNodeType(final String propertyName,
342                                                                                  final PropertyDefinition propertyDefinition,
343                                                                                  final NodeTypeEnum nodeType, final String uniqueId,
344                                                                                  final boolean inTransaction) {
345         List<PropertyConstraint> constraints = propertyDefinition.getConstraints();
346         propertyDefinition.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(uniqueId, propertyName));
347         PropertyData propertyData = new PropertyData(propertyDefinition, convertConstraintsToString(constraints));
348         log.debug(BEFORE_ADDING_PROPERTY_TO_GRAPH, propertyData);
349         Either<PropertyData, JanusGraphOperationStatus> createNodeResult = janusGraphGenericDao.createNode(propertyData, PropertyData.class);
350         log.debug(AFTER_ADDING_PROPERTY_TO_GRAPH, propertyData);
351         if (createNodeResult.isRight()) {
352             if (!inTransaction) {
353                 janusGraphGenericDao.rollback();
354             }
355             JanusGraphOperationStatus operationStatus = createNodeResult.right().value();
356             log.error("Failed to add property {} to graph. status is {}", propertyName, operationStatus);
357             return Either.right(operationStatus);
358         }
359         Map<String, Object> props = new HashMap<>();
360         props.put(GraphPropertiesDictionary.NAME.getProperty(), propertyName);
361         UniqueIdData uniqueIdData = new UniqueIdData(nodeType, uniqueId);
362         log.debug("Before associating {} to property {}", uniqueIdData, propertyName);
363         Either<GraphRelation, JanusGraphOperationStatus> createRelResult =
364             janusGraphGenericDao.createRelation(uniqueIdData, propertyData, GraphEdgeLabels.PROPERTY, props);
365         if (createRelResult.isRight()) {
366             if (!inTransaction) {
367                 janusGraphGenericDao.rollback();
368             }
369             JanusGraphOperationStatus operationStatus = createNodeResult.right().value();
370             log.error(FAILED_TO_ASSOCIATE_RESOURCE_TO_PROPERTY_IN_GRAPH_STATUS_IS, uniqueId, propertyName, operationStatus);
371             return Either.right(operationStatus);
372         }
373         if (!inTransaction) {
374             janusGraphGenericDao.commit();
375         }
376         return Either.left(createNodeResult.left().value());
377     }
378
379     public Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesOfNode(NodeTypeEnum nodeType, String uniqueId) {
380         Map<String, PropertyDefinition> resourceProps = new HashMap<>();
381         Either<List<ImmutablePair<PropertyData, GraphEdge>>, JanusGraphOperationStatus> childrenNodes = janusGraphGenericDao
382             .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), uniqueId, GraphEdgeLabels.PROPERTY, NodeTypeEnum.Property,
383                 PropertyData.class);
384         if (childrenNodes.isRight()) {
385             JanusGraphOperationStatus operationStatus = childrenNodes.right().value();
386             return Either.right(operationStatus);
387         }
388         List<ImmutablePair<PropertyData, GraphEdge>> values = childrenNodes.left().value();
389         if (values != null) {
390             for (ImmutablePair<PropertyData, GraphEdge> immutablePair : values) {
391                 GraphEdge edge = immutablePair.getValue();
392                 String propertyName = (String) edge.getProperties().get(GraphPropertiesDictionary.NAME.getProperty());
393                 log.debug("Property {} is associated to node {}", propertyName, uniqueId);
394                 PropertyData propertyData = immutablePair.getKey();
395                 PropertyDefinition propertyDefinition = this.convertPropertyDataToPropertyDefinition(propertyData, propertyName, uniqueId);
396                 resourceProps.put(propertyName, propertyDefinition);
397             }
398         }
399         log.debug("The properties associated to node {} are {}", uniqueId, resourceProps);
400         return Either.left(resourceProps);
401     }
402
403     public Either<Map<String, PropertyDefinition>, StorageOperationStatus> deletePropertiesAssociatedToNode(NodeTypeEnum nodeType, String uniqueId) {
404         return deleteAllPropertiesAssociatedToNode(nodeType, uniqueId).right()
405             .bind(err -> err == StorageOperationStatus.OK ? Either.left(Collections.emptyMap()) : Either.right(err));
406     }
407
408     public Either<Map<String, PropertyData>, JanusGraphOperationStatus> mergePropertiesAssociatedToNode(NodeTypeEnum nodeType, String uniqueId,
409                                                                                                         Map<String, PropertyDefinition> newProperties) {
410         Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> oldPropertiesRes = findPropertiesOfNode(nodeType, uniqueId);
411         Map<String, PropertyDefinition> reallyNewProperties;
412         Map<String, PropertyData> unchangedPropsData;
413         if (oldPropertiesRes.isRight()) {
414             JanusGraphOperationStatus err = oldPropertiesRes.right().value();
415             if (err == JanusGraphOperationStatus.NOT_FOUND) {
416                 reallyNewProperties = newProperties;
417                 unchangedPropsData = Collections.emptyMap();
418             } else {
419                 return Either.right(err);
420             }
421         } else {
422             Map<String, PropertyDefinition> oldProperties = oldPropertiesRes.left().value();
423             reallyNewProperties = collectReallyNewProperties(newProperties, oldProperties);
424             for (Entry<String, PropertyDefinition> oldEntry : oldProperties.entrySet()) {
425                 String key = oldEntry.getKey();
426                 PropertyDefinition newPropDef = newProperties != null ? newProperties.get(key) : null;
427                 PropertyDefinition oldPropDef = oldEntry.getValue();
428                 JanusGraphOperationStatus status = updateOldProperty(newPropDef, oldPropDef);
429                 if (status != JanusGraphOperationStatus.OK) {
430                     return Either.right(status);
431                 }
432             }
433             unchangedPropsData = oldProperties.entrySet().stream()
434                 .collect(Collectors.toMap(Entry::getKey, e -> new PropertyData(e.getValue(), null)));
435         }
436         // add other properties
437         return addPropertiesToElementType(nodeType, uniqueId, reallyNewProperties, unchangedPropsData);
438     }
439
440     /**
441      * @param newProperties
442      * @param oldProperties
443      * @return
444      */
445     private Map<String, PropertyDefinition> collectReallyNewProperties(Map<String, PropertyDefinition> newProperties,
446                                                                        Map<String, PropertyDefinition> oldProperties) {
447         return newProperties != null ? newProperties.entrySet().stream().filter(entry -> !oldProperties.containsKey(entry.getKey()))
448             .collect(Collectors.toMap(Entry::getKey, Entry::getValue)) : null;
449     }
450
451     /**
452      * @param newPropDef
453      * @param oldPropDef
454      */
455     private JanusGraphOperationStatus updateOldProperty(PropertyDefinition newPropDef, PropertyDefinition oldPropDef) {
456         if (!isUpdateAllowed(newPropDef, oldPropDef)) {
457             return JanusGraphOperationStatus.MATCH_NOT_FOUND;
458         }
459         if (isUpdateRequired(newPropDef, oldPropDef)) {
460             modifyOldPropByNewOne(newPropDef, oldPropDef);
461             List<PropertyConstraint> constraints = oldPropDef.getConstraints();
462             PropertyData node = new PropertyData(oldPropDef, convertConstraintsToString(constraints));
463             Either<PropertyData, JanusGraphOperationStatus> updateResult = janusGraphGenericDao.updateNode(node, PropertyData.class);
464             if (updateResult.isRight()) {
465                 return updateResult.right().value();
466             }
467         }
468         return JanusGraphOperationStatus.OK;
469     }
470
471     /**
472      * @param newPropDef
473      * @param oldPropDef
474      */
475     private boolean isUpdateAllowed(PropertyDefinition newPropDef, PropertyDefinition oldPropDef) {
476         if (newPropDef == null) {
477             log.error("#mergePropertiesAssociatedToNode - Failed due attempt to delete the property with id {}", oldPropDef.getUniqueId());
478             return false;
479         }
480         // If the property type is missing it's something that we could want to fix
481         if (oldPropDef.getType() != null && !oldPropDef.getType().equals(newPropDef.getType())) {
482             log.error("#mergePropertiesAssociatedToNode - Failed due attempt to change type of the property with id {}", oldPropDef.getUniqueId());
483             return false;
484         }
485         return true;
486     }
487
488     /**
489      * Update only fields which modification is permitted.
490      *
491      * @param newPropDef
492      * @param oldPropDef
493      */
494     private void modifyOldPropByNewOne(PropertyDefinition newPropDef, PropertyDefinition oldPropDef) {
495         oldPropDef.setDefaultValue(newPropDef.getDefaultValue());
496         oldPropDef.setDescription(newPropDef.getDescription());
497         oldPropDef.setRequired(newPropDef.isRequired());
498         // Type is updated to fix possible null type issue in janusGraph DB
499         oldPropDef.setType(newPropDef.getType());
500     }
501
502     private boolean isUpdateRequired(PropertyDefinition newPropDef, PropertyDefinition oldPropDef) {
503         return !StringUtils.equals(oldPropDef.getDefaultValue(), newPropDef.getDefaultValue()) || !StringUtils
504             .equals(oldPropDef.getDescription(), newPropDef.getDescription()) || oldPropDef.isRequired() != newPropDef.isRequired();
505     }
506
507     /**
508      * Adds newProperties and returns in case of success (left part of Either) map of all properties i. e. added ones and contained in
509      * unchangedPropsData
510      *
511      * @param nodeType
512      * @param uniqueId
513      * @param newProperties
514      * @param unchangedPropsData
515      * @return
516      */
517     private Either<Map<String, PropertyData>, JanusGraphOperationStatus> addPropertiesToElementType(NodeTypeEnum nodeType, String uniqueId,
518                                                                                                     Map<String, PropertyDefinition> newProperties,
519                                                                                                     Map<String, PropertyData> unchangedPropsData) {
520         return addPropertiesToElementType(uniqueId, nodeType, newProperties).left().map(m -> {
521             m.putAll(unchangedPropsData);
522             return m;
523         });
524     }
525
526     public Either<Map<String, PropertyDefinition>, StorageOperationStatus> deleteAllPropertiesAssociatedToNode(NodeTypeEnum nodeType,
527                                                                                                                String uniqueId) {
528         Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> propertiesOfNodeRes = findPropertiesOfNode(nodeType, uniqueId);
529         if (propertiesOfNodeRes.isRight()) {
530             JanusGraphOperationStatus status = propertiesOfNodeRes.right().value();
531             if (status == JanusGraphOperationStatus.NOT_FOUND) {
532                 return Either.right(StorageOperationStatus.OK);
533             }
534             return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(status));
535         }
536         Map<String, PropertyDefinition> value = propertiesOfNodeRes.left().value();
537         for (PropertyDefinition propertyDefinition : value.values()) {
538             String propertyUid = propertyDefinition.getUniqueId();
539             Either<PropertyData, JanusGraphOperationStatus> deletePropertyRes = deletePropertyFromGraph(propertyUid);
540             if (deletePropertyRes.isRight()) {
541                 log.error("Failed to delete property with id {}", propertyUid);
542                 JanusGraphOperationStatus status = deletePropertyRes.right().value();
543                 if (status == JanusGraphOperationStatus.NOT_FOUND) {
544                     status = JanusGraphOperationStatus.INVALID_ID;
545                 }
546                 return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(status));
547             }
548         }
549         log.debug("The properties deleted from node {} are {}", uniqueId, value);
550         return Either.left(value);
551     }
552
553     /**
554      * Checks existence of a property with the same name belonging to the same resource or existence of property with the same name and different type
555      * (including derived from hierarchy)
556      *
557      * @param properties
558      * @param resourceUid
559      * @param propertyName
560      * @param propertyType
561      * @return
562      */
563     public boolean isPropertyExist(List<PropertyDefinition> properties, String resourceUid, String propertyName, String propertyType) {
564         boolean result = false;
565         if (!CollectionUtils.isEmpty(properties)) {
566             for (PropertyDefinition propertyDefinition : properties) {
567                 if (propertyDefinition.getName().equals(propertyName) && (propertyDefinition.getParentUniqueId().equals(resourceUid)
568                     || !propertyDefinition.getType().equals(propertyType))) {
569                     result = true;
570                     break;
571                 }
572             }
573         }
574         return result;
575     }
576
577     public ImmutablePair<String, Boolean> validateAndUpdateRules(String propertyType, List<PropertyRule> rules, String innerType,
578                                                                  Map<String, DataTypeDefinition> dataTypes, boolean isValidate) {
579         if (rules == null || rules.isEmpty()) {
580             return new ImmutablePair<>(null, true);
581         }
582         for (PropertyRule rule : rules) {
583             String value = rule.getValue();
584             Either<Object, Boolean> updateResult = validateAndUpdatePropertyValue(propertyType, value, isValidate, innerType, dataTypes);
585             if (updateResult.isRight()) {
586                 Boolean status = updateResult.right().value();
587                 if (!status) {
588                     return new ImmutablePair<>(value, status);
589                 }
590             } else {
591                 String newValue = null;
592                 Object object = updateResult.left().value();
593                 if (object != null) {
594                     newValue = object.toString();
595                 }
596                 rule.setValue(newValue);
597             }
598         }
599         return new ImmutablePair<>(null, true);
600     }
601
602     public void addRulesToNewPropertyValue(PropertyValueData propertyValueData, ComponentInstanceProperty resourceInstanceProperty,
603                                            String resourceInstanceId) {
604         List<PropertyRule> rules = resourceInstanceProperty.getRules();
605         if (rules == null) {
606             PropertyRule propertyRule = buildRuleFromPath(propertyValueData, resourceInstanceProperty, resourceInstanceId);
607             rules = new ArrayList<>();
608             rules.add(propertyRule);
609         } else {
610             rules = sortRules(rules);
611         }
612         propertyValueData.setRules(rules);
613     }
614
615     private PropertyRule buildRuleFromPath(PropertyValueData propertyValueData, ComponentInstanceProperty resourceInstanceProperty,
616                                            String resourceInstanceId) {
617         List<String> path = resourceInstanceProperty.getPath();
618         // FOR BC. Since old Property values on VFC/VF does not have rules on
619
620         // graph.
621
622         // Update could be done on one level only, thus we can use this
623
624         // operation to avoid migration.
625         if (path == null || path.isEmpty()) {
626             path = new ArrayList<>();
627             path.add(resourceInstanceId);
628         }
629         PropertyRule propertyRule = new PropertyRule();
630         propertyRule.setRule(path);
631         propertyRule.setValue(propertyValueData.getValue());
632         return propertyRule;
633     }
634
635     private List<PropertyRule> sortRules(List<PropertyRule> rules) {
636         // TODO: sort the rules by size and binary representation.
637
638         // (x, y, .+) --> 110 6 priority 1
639
640         // (x, .+, z) --> 101 5 priority 2
641         return rules;
642     }
643
644     public ImmutablePair<JanusGraphOperationStatus, String> findPropertyValue(String resourceInstanceId, String propertyId) {
645         log.debug("Going to check whether the property {} already added to resource instance {}", propertyId, resourceInstanceId);
646         Either<List<ComponentInstanceProperty>, JanusGraphOperationStatus> getAllRes = this
647             .getAllPropertiesOfResourceInstanceOnlyPropertyDefId(resourceInstanceId);
648         if (getAllRes.isRight()) {
649             JanusGraphOperationStatus status = getAllRes.right().value();
650             log.trace("After fetching all properties of resource instance {}. Status is {}", resourceInstanceId, status);
651             return new ImmutablePair<>(status, null);
652         }
653         List<ComponentInstanceProperty> list = getAllRes.left().value();
654         if (list != null) {
655             for (ComponentInstanceProperty instanceProperty : list) {
656                 String propertyUniqueId = instanceProperty.getUniqueId();
657                 String valueUniqueUid = instanceProperty.getValueUniqueUid();
658                 log.trace("Go over property {} under resource instance {}. valueUniqueId = {}", propertyUniqueId, resourceInstanceId, valueUniqueUid);
659                 if (propertyId.equals(propertyUniqueId) && valueUniqueUid != null) {
660                     log.debug("The property {} already created under resource instance {}", propertyId, resourceInstanceId);
661                     return new ImmutablePair<>(JanusGraphOperationStatus.ALREADY_EXIST, valueUniqueUid);
662                 }
663             }
664         }
665         return new ImmutablePair<>(JanusGraphOperationStatus.NOT_FOUND, null);
666     }
667
668     public void updateRulesInPropertyValue(PropertyValueData propertyValueData, ComponentInstanceProperty resourceInstanceProperty,
669                                            String resourceInstanceId) {
670         List<PropertyRule> currentRules = propertyValueData.getRules();
671         List<PropertyRule> rules = resourceInstanceProperty.getRules();
672         // if rules are not supported.
673         if (rules == null) {
674             PropertyRule propertyRule = buildRuleFromPath(propertyValueData, resourceInstanceProperty, resourceInstanceId);
675             rules = new ArrayList<>();
676             rules.add(propertyRule);
677             if (currentRules != null) {
678                 rules = mergeRules(currentRules, rules);
679             }
680         } else {
681             // Full mode. all rules are sent in update operation.
682             rules = sortRules(rules);
683         }
684         propertyValueData.setRules(rules);
685     }
686
687     private List<PropertyRule> mergeRules(List<PropertyRule> currentRules, List<PropertyRule> newRules) {
688         List<PropertyRule> mergedRules = new ArrayList<>();
689         if (newRules == null || newRules.isEmpty()) {
690             return currentRules;
691         }
692         for (PropertyRule rule : currentRules) {
693             PropertyRule propertyRule = new PropertyRule(rule.getRule(), rule.getValue());
694             mergedRules.add(propertyRule);
695         }
696         for (PropertyRule rule : newRules) {
697             PropertyRule foundRule = findRuleInList(rule, mergedRules);
698             if (foundRule != null) {
699                 foundRule.setValue(rule.getValue());
700             } else {
701                 mergedRules.add(rule);
702             }
703         }
704         return mergedRules;
705     }
706
707     private PropertyRule findRuleInList(PropertyRule rule, List<PropertyRule> rules) {
708         if (rules == null || rules.isEmpty() || rule.getRule() == null || rule.getRule().isEmpty()) {
709             return null;
710         }
711         PropertyRule foundRule = null;
712         for (PropertyRule propertyRule : rules) {
713             if (rule.getRuleSize() != propertyRule.getRuleSize()) {
714                 continue;
715             }
716             boolean equals = propertyRule.compareRule(rule);
717             if (equals) {
718                 foundRule = propertyRule;
719                 break;
720             }
721         }
722         return foundRule;
723     }
724
725     /**
726      * return all properties associated to resource instance. The result does contains the property unique id but not its type, default value...
727      *
728      * @param resourceInstanceUid
729      * @return
730      */
731     public Either<List<ComponentInstanceProperty>, JanusGraphOperationStatus> getAllPropertiesOfResourceInstanceOnlyPropertyDefId(
732         String resourceInstanceUid) {
733         return getAllPropertiesOfResourceInstanceOnlyPropertyDefId(resourceInstanceUid, NodeTypeEnum.ResourceInstance);
734     }
735
736     public Either<PropertyValueData, JanusGraphOperationStatus> removePropertyOfResourceInstance(String propertyValueUid, String resourceInstanceId) {
737         Either<ComponentInstanceData, JanusGraphOperationStatus> findResInstanceRes = janusGraphGenericDao
738             .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.ResourceInstance), resourceInstanceId, ComponentInstanceData.class);
739         if (findResInstanceRes.isRight()) {
740             JanusGraphOperationStatus status = findResInstanceRes.right().value();
741             if (status == JanusGraphOperationStatus.NOT_FOUND) {
742                 status = JanusGraphOperationStatus.INVALID_ID;
743             }
744             return Either.right(status);
745         }
746         Either<PropertyValueData, JanusGraphOperationStatus> findPropertyDefRes = janusGraphGenericDao
747             .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.PropertyValue), propertyValueUid, PropertyValueData.class);
748         if (findPropertyDefRes.isRight()) {
749             JanusGraphOperationStatus status = findPropertyDefRes.right().value();
750             if (status == JanusGraphOperationStatus.NOT_FOUND) {
751                 status = JanusGraphOperationStatus.INVALID_ID;
752             }
753             return Either.right(status);
754         }
755         Either<GraphRelation, JanusGraphOperationStatus> relation = janusGraphGenericDao
756             .getRelation(findResInstanceRes.left().value(), findPropertyDefRes.left().value(), GraphEdgeLabels.PROPERTY_VALUE);
757         if (relation.isRight()) {
758             // TODO: add error in case of error
759             JanusGraphOperationStatus status = relation.right().value();
760             if (status == JanusGraphOperationStatus.NOT_FOUND) {
761                 status = JanusGraphOperationStatus.INVALID_ID;
762             }
763             return Either.right(status);
764         }
765         Either<PropertyValueData, JanusGraphOperationStatus> deleteNode = janusGraphGenericDao
766             .deleteNode(findPropertyDefRes.left().value(), PropertyValueData.class);
767         if (deleteNode.isRight()) {
768             return Either.right(deleteNode.right().value());
769         }
770         PropertyValueData value = deleteNode.left().value();
771         return Either.left(value);
772     }
773
774     public Either<ComponentInstanceProperty, StorageOperationStatus> removePropertyValueFromResourceInstance(String propertyValueUid,
775                                                                                                              String resourceInstanceId,
776                                                                                                              boolean inTransaction) {
777         Either<ComponentInstanceProperty, StorageOperationStatus> result = null;
778         try {
779             Either<PropertyValueData, JanusGraphOperationStatus> eitherStatus = this
780                 .removePropertyOfResourceInstance(propertyValueUid, resourceInstanceId);
781             if (eitherStatus.isRight()) {
782                 log.error("Failed to remove property value {} from resource instance {} in Graph. status is {}", propertyValueUid, resourceInstanceId,
783                     eitherStatus.right().value().name());
784                 result = Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(eitherStatus.right().value()));
785                 return result;
786             } else {
787                 PropertyValueData propertyValueData = eitherStatus.left().value();
788                 ComponentInstanceProperty propertyValueResult = new ComponentInstanceProperty();
789                 propertyValueResult.setUniqueId(resourceInstanceId);
790                 propertyValueResult.setValue(propertyValueData.getValue());
791                 log.debug("The returned ResourceInstanceProperty is  {}", propertyValueResult);
792                 result = Either.left(propertyValueResult);
793                 return result;
794             }
795         } finally {
796             if (!inTransaction) {
797                 if (result == null || result.isRight()) {
798                     log.error(GOING_TO_EXECUTE_ROLLBACK_ON_GRAPH);
799                     janusGraphGenericDao.rollback();
800                 } else {
801                     log.debug(GOING_TO_EXECUTE_COMMIT_ON_GRAPH);
802                     janusGraphGenericDao.commit();
803                 }
804             }
805         }
806     }
807
808     public ComponentInstanceProperty buildResourceInstanceProperty(PropertyValueData propertyValueData,
809                                                                    ComponentInstanceProperty resourceInstanceProperty) {
810         String value = propertyValueData.getValue();
811         String uid = propertyValueData.getUniqueId();
812         ComponentInstanceProperty instanceProperty = new ComponentInstanceProperty(resourceInstanceProperty, value, uid);
813         instanceProperty.setPath(resourceInstanceProperty.getPath());
814         return instanceProperty;
815     }
816
817     @Override
818     public boolean isPropertyDefaultValueValid(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
819         if (propertyDefinition == null) {
820             return false;
821         }
822         String innerType = null;
823         String propertyType = propertyDefinition.getType();
824         ToscaPropertyType type = getType(propertyType);
825         if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
826             SchemaDefinition def = propertyDefinition.getSchema();
827             if (def == null) {
828                 return false;
829             }
830             PropertyDataDefinition propDef = def.getProperty();
831             if (propDef == null) {
832                 return false;
833             }
834             innerType = propDef.getType();
835         }
836         String value = propertyDefinition.getDefaultValue();
837         if (type != null) {
838             return isValidValue(type, value, innerType, dataTypes);
839         } else {
840             log.trace("The given type {} is not a pre defined one.", propertyType);
841             DataTypeDefinition foundDt = dataTypes.get(propertyType);
842             if (foundDt != null) {
843                 return isValidComplexValue(foundDt, value, dataTypes);
844             } else {
845                 return false;
846             }
847         }
848     }
849
850     public boolean isPropertyTypeValid(final IComplexDefaultValue property, final String model) {
851         if (property == null) {
852             return false;
853         }
854         if (ToscaPropertyType.isValidType(property.getType()) == null) {
855             Either<Boolean, JanusGraphOperationStatus> definedInDataTypes = isDefinedInDataTypes(property.getType(), model);
856             if (definedInDataTypes.isRight()) {
857                 return false;
858             } else {
859                 Boolean isExist = definedInDataTypes.left().value();
860                 return isExist.booleanValue();
861             }
862         }
863         return true;
864     }
865
866     public boolean isPropertyTypeValid(final IComplexDefaultValue property, final Map<String, DataTypeDefinition> dataTypes) {
867         if (property == null) {
868             return false;
869         }
870         return ToscaPropertyType.isValidType(property.getType()) != null || dataTypes.containsKey(property.getType());
871     }
872
873     @Override
874     public ImmutablePair<String, Boolean> isPropertyInnerTypeValid(IComplexDefaultValue property, Map<String, DataTypeDefinition> dataTypes) {
875         if (property == null) {
876             return new ImmutablePair<>(null, false);
877         }
878         SchemaDefinition schema;
879         PropertyDataDefinition innerProp;
880         String innerType = null;
881         if ((schema = property.getSchema()) != null) {
882             if ((innerProp = schema.getProperty()) != null) {
883                 innerType = innerProp.getType();
884             }
885         }
886         ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(innerType);
887         if (innerToscaType == null) {
888             DataTypeDefinition dataTypeDefinition = dataTypes.get(innerType);
889             if (dataTypeDefinition == null) {
890                 log.debug("The inner type {} is not a data type.", innerType);
891                 return new ImmutablePair<>(innerType, false);
892             } else {
893                 log.debug("The inner type {} is a data type. Data type definition is {}", innerType, dataTypeDefinition);
894             }
895         }
896         return new ImmutablePair<>(innerType, true);
897     }
898
899     private boolean isValidComplexValue(DataTypeDefinition foundDt, String value, Map<String, DataTypeDefinition> dataTypes) {
900         ImmutablePair<JsonElement, Boolean> validateAndUpdate = dataTypeValidatorConverter.validateAndUpdate(value, foundDt, dataTypes);
901         log.trace("The result after validating complex value of type {} is {}", foundDt.getName(), validateAndUpdate);
902         return validateAndUpdate.right.booleanValue();
903     }
904
905     public Either<List<ComponentInstanceProperty>, JanusGraphOperationStatus> getAllPropertiesOfResourceInstanceOnlyPropertyDefId(
906         String resourceInstanceUid, NodeTypeEnum instanceNodeType) {
907         Either<JanusGraphVertex, JanusGraphOperationStatus> findResInstanceRes = janusGraphGenericDao
908             .getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(instanceNodeType), resourceInstanceUid);
909         if (findResInstanceRes.isRight()) {
910             JanusGraphOperationStatus status = findResInstanceRes.right().value();
911             if (status == JanusGraphOperationStatus.NOT_FOUND) {
912                 status = JanusGraphOperationStatus.INVALID_ID;
913             }
914             return Either.right(status);
915         }
916         Either<List<ImmutablePair<JanusGraphVertex, Edge>>, JanusGraphOperationStatus> propertyImplNodes = janusGraphGenericDao
917             .getChildrenVertecies(UniqueIdBuilder.getKeyByNodeType(instanceNodeType), resourceInstanceUid, GraphEdgeLabels.PROPERTY_VALUE);
918         if (propertyImplNodes.isRight()) {
919             JanusGraphOperationStatus status = propertyImplNodes.right().value();
920             return Either.right(status);
921         }
922         List<ImmutablePair<JanusGraphVertex, Edge>> list = propertyImplNodes.left().value();
923         if (list == null || list.isEmpty()) {
924             return Either.right(JanusGraphOperationStatus.NOT_FOUND);
925         }
926         List<ComponentInstanceProperty> result = new ArrayList<>();
927         for (ImmutablePair<JanusGraphVertex, Edge> propertyValue : list) {
928             JanusGraphVertex propertyValueDataVertex = propertyValue.getLeft();
929             String propertyValueUid = (String) janusGraphGenericDao
930                 .getProperty(propertyValueDataVertex, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
931             String value = (String) janusGraphGenericDao.getProperty(propertyValueDataVertex, GraphPropertiesDictionary.VALUE.getProperty());
932             ImmutablePair<JanusGraphVertex, Edge> propertyDefPair = janusGraphGenericDao
933                 .getChildVertex(propertyValueDataVertex, GraphEdgeLabels.PROPERTY_IMPL);
934             if (propertyDefPair == null) {
935                 return Either.right(JanusGraphOperationStatus.NOT_FOUND);
936             }
937             Map<String, Object> properties = janusGraphGenericDao.getProperties(propertyValueDataVertex);
938             PropertyValueData propertyValueData = GraphElementFactory
939                 .createElement(NodeTypeEnum.PropertyValue.getName(), GraphElementTypeEnum.Node, properties, PropertyValueData.class);
940             String propertyUniqueId = (String) janusGraphGenericDao
941                 .getProperty(propertyDefPair.left, GraphPropertiesDictionary.UNIQUE_ID.getProperty());
942             ComponentInstanceProperty resourceInstanceProperty = new ComponentInstanceProperty();
943             // set property original unique id
944             resourceInstanceProperty.setUniqueId(propertyUniqueId);
945             // set resource id
946
947             // TODO: esofer add resource id
948             resourceInstanceProperty.setParentUniqueId(null);
949             // set value
950             resourceInstanceProperty.setValue(value);
951             // set property value unique id
952             resourceInstanceProperty.setValueUniqueUid(propertyValueUid);
953             // set rules
954             resourceInstanceProperty.setRules(propertyValueData.getRules());
955             result.add(resourceInstanceProperty);
956         }
957         return Either.left(result);
958     }
959
960     /**
961      * Find the default value from the list of component instances. Start the search from the second component instance
962      *
963      * @param pathOfComponentInstances
964      * @param propertyUniqueId
965      * @param defaultValue
966      * @return
967      */
968     public Either<String, JanusGraphOperationStatus> findDefaultValueFromSecondPosition(List<String> pathOfComponentInstances,
969                                                                                         String propertyUniqueId, String defaultValue) {
970         log.trace("In find default value: path= {} propertyUniqId={} defaultValue= {}", pathOfComponentInstances, propertyUniqueId, defaultValue);
971         if (pathOfComponentInstances == null || pathOfComponentInstances.size() < 2) {
972             return Either.left(defaultValue);
973         }
974         String result = defaultValue;
975         for (int i = 1; i < pathOfComponentInstances.size(); i++) {
976             String compInstanceId = pathOfComponentInstances.get(i);
977             Either<List<ComponentInstanceProperty>, JanusGraphOperationStatus> propertyValuesResult = this
978                 .getAllPropertiesOfResourceInstanceOnlyPropertyDefId(compInstanceId, NodeTypeEnum.ResourceInstance);
979             log.trace("After fetching properties values of component instance {}. {}", compInstanceId, propertyValuesResult);
980             if (propertyValuesResult.isRight()) {
981                 JanusGraphOperationStatus status = propertyValuesResult.right().value();
982                 if (status != JanusGraphOperationStatus.NOT_FOUND) {
983                     return Either.right(status);
984                 } else {
985                     continue;
986                 }
987             }
988             ComponentInstanceProperty foundCompInstanceProperty = fetchByPropertyUid(propertyValuesResult.left().value(), propertyUniqueId);
989             log.trace("After finding the component instance property on{} . {}", compInstanceId, foundCompInstanceProperty);
990             if (foundCompInstanceProperty == null) {
991                 continue;
992             }
993             List<PropertyRule> rules = getOrBuildRulesIfNotExists(pathOfComponentInstances.size() - i, pathOfComponentInstances.get(i),
994                 foundCompInstanceProperty.getRules(), foundCompInstanceProperty.getValue());
995             log.trace("Rules of property {} on component instance {} are {}", propertyUniqueId, compInstanceId, rules);
996             PropertyRule matchedRule = findMatchRule(pathOfComponentInstances, i, rules);
997             log.trace("Match rule is {}", matchedRule);
998             if (matchedRule != null) {
999                 result = matchedRule.getValue();
1000                 break;
1001             }
1002         }
1003         return Either.left(result);
1004     }
1005
1006     private ComponentInstanceProperty fetchByPropertyUid(List<ComponentInstanceProperty> list, String propertyUniqueId) {
1007         ComponentInstanceProperty result = null;
1008         if (list == null) {
1009             return null;
1010         }
1011         for (ComponentInstanceProperty instProperty : list) {
1012             if (instProperty.getUniqueId().equals(propertyUniqueId)) {
1013                 result = instProperty;
1014                 break;
1015             }
1016         }
1017         return result;
1018     }
1019
1020     private List<PropertyRule> getOrBuildRulesIfNotExists(int ruleSize, String compInstanceId, List<PropertyRule> rules, String value) {
1021         if (rules != null) {
1022             return rules;
1023         }
1024         rules = buildDefaultRule(compInstanceId, ruleSize, value);
1025         return rules;
1026     }
1027
1028     private List<PropertyRule> getRulesOfPropertyValue(int size, String instanceId, ComponentInstanceProperty componentInstanceProperty) {
1029         List<PropertyRule> rules = componentInstanceProperty.getRules();
1030         if (rules == null) {
1031             rules = buildDefaultRule(instanceId, size, componentInstanceProperty.getValue());
1032         }
1033         return rules;
1034     }
1035
1036     private List<PropertyRule> buildDefaultRule(String componentInstanceId, int size, String value) {
1037         List<PropertyRule> rules = new ArrayList<>();
1038         List<String> rule = new ArrayList<>();
1039         rule.add(componentInstanceId);
1040         for (int i = 0; i < size - 1; i++) {
1041             rule.add(PropertyRule.getRuleAnyMatch());
1042         }
1043         PropertyRule propertyRule = new PropertyRule(rule, value);
1044         rules.add(propertyRule);
1045         return rules;
1046     }
1047
1048     private PropertyRule findMatchRule(List<String> pathOfInstances, int level, List<PropertyRule> rules) {
1049         PropertyRule propertyRule = null;
1050         String stringForMatch = buildStringForMatch(pathOfInstances, level);
1051         String firstCompInstance = pathOfInstances.get(level);
1052         if (rules != null) {
1053             for (PropertyRule rule : rules) {
1054                 int ruleSize = rule.getRule().size();
1055                 // check the length of the rule equals to the length of the
1056
1057                 // instances path.
1058                 if (ruleSize != pathOfInstances.size() - level) {
1059                     continue;
1060                 }
1061                 // check that the rule starts with correct component instance id
1062                 if (!checkFirstItem(firstCompInstance, rule.getFirstToken())) {
1063                     continue;
1064                 }
1065                 String secondToken = rule.getToken(2);
1066                 if (secondToken != null && (secondToken.equals(PropertyRule.getForceAll()) || secondToken.equals(PropertyRule.getALL()))) {
1067                     propertyRule = rule;
1068                     break;
1069                 }
1070                 String patternStr = buildStringForMatch(rule.getRule(), 0);
1071                 Pattern pattern = Pattern.compile(patternStr);
1072                 Matcher matcher = pattern.matcher(stringForMatch);
1073                 if (matcher.matches()) {
1074                     if (log.isTraceEnabled()) {
1075                         log.trace("{} matches the rule {}", stringForMatch, patternStr);
1076                     }
1077                     propertyRule = rule;
1078                     break;
1079                 }
1080             }
1081         }
1082         return propertyRule;
1083     }
1084
1085     private boolean checkFirstItem(String left, String right) {
1086         if (left != null && left.equals(right)) {
1087             return true;
1088         }
1089         return false;
1090     }
1091
1092     private String buildStringForMatch(List<String> pathOfInstances, int level) {
1093         StringBuilder builder = new StringBuilder();
1094         for (int i = level; i < pathOfInstances.size(); i++) {
1095             builder.append(pathOfInstances.get(i));
1096             if (i < pathOfInstances.size() - 1) {
1097                 builder.append("#");
1098             }
1099         }
1100         return builder.toString();
1101     }
1102
1103     public void updatePropertyByBestMatch(String propertyUniqueId, ComponentInstanceProperty instanceProperty,
1104                                           Map<String, ComponentInstanceProperty> instanceIdToValue) {
1105         List<String> pathOfInstances = instanceProperty.getPath();
1106         int level = 0;
1107         int size = pathOfInstances.size();
1108         int numberOfMatches = 0;
1109         for (String instanceId : pathOfInstances) {
1110             ComponentInstanceProperty componentInstanceProperty = instanceIdToValue.get(instanceId);
1111             if (componentInstanceProperty != null) {
1112                 List<PropertyRule> rules = getRulesOfPropertyValue(size - level, instanceId, componentInstanceProperty);
1113                 // If it is the first level instance, then update valueUniuqeId
1114
1115                 // parameter in order to know on update that
1116
1117                 // we should update and not create new node on graph.
1118                 if (level == 0) {
1119                     instanceProperty.setValueUniqueUid(componentInstanceProperty.getValueUniqueUid());
1120                     instanceProperty.setRules(rules);
1121                 }
1122                 PropertyRule rule = findMatchRule(pathOfInstances, level, rules);
1123                 if (rule != null) {
1124                     numberOfMatches++;
1125                     String value = rule.getValue();
1126                     if (numberOfMatches == 1) {
1127                         instanceProperty.setValue(value);
1128                         if (log.isDebugEnabled()) {
1129                             log.debug("Set the value of property {} {} on path {} to be {}", propertyUniqueId, instanceProperty.getName(),
1130                                 pathOfInstances, value);
1131                         }
1132                     } else if (numberOfMatches == 2) {
1133                         // In case of another property value match, then use the
1134
1135                         // value to be the default value of the property.
1136                         instanceProperty.setDefaultValue(value);
1137                         if (log.isDebugEnabled()) {
1138                             log.debug("Set the default value of property {} {} on path {} to be {}", propertyUniqueId, instanceProperty.getName(),
1139                                 pathOfInstances, value);
1140                         }
1141                         break;
1142                     }
1143                 }
1144             }
1145             level++;
1146         }
1147     }
1148
1149     /**
1150      * Add data type to graph.
1151      * <p>
1152      * 1. Add data type node
1153      * <p>
1154      * 2. Add edge between the former node to its parent(if exists)
1155      * <p>
1156      * 3. Add property node and associate it to the node created at #1. (per property & if exists)
1157      *
1158      * @param dataTypeDefinition
1159      * @return
1160      */
1161     private Either<DataTypeData, JanusGraphOperationStatus> addDataTypeToGraph(DataTypeDefinition dataTypeDefinition) {
1162         log.debug("Got data type {}", dataTypeDefinition);
1163         String dtUniqueId = UniqueIdBuilder.buildDataTypeUid(dataTypeDefinition.getModel(), dataTypeDefinition.getName());
1164         DataTypeData dataTypeData = buildDataTypeData(dataTypeDefinition, dtUniqueId);
1165         log.debug("Before adding data type to graph. dataTypeData = {}", dataTypeData);
1166         Either<DataTypeData, JanusGraphOperationStatus> createDataTypeResult = janusGraphGenericDao.createNode(dataTypeData, DataTypeData.class);
1167         log.debug("After adding data type to graph. status is = {}", createDataTypeResult);
1168         if (createDataTypeResult.isRight()) {
1169             JanusGraphOperationStatus operationStatus = createDataTypeResult.right().value();
1170             log.debug("Failed to data type {} to graph. status is {}", dataTypeDefinition.getName(), operationStatus);
1171             BeEcompErrorManager.getInstance().logBeFailedAddingNodeTypeError("AddDataType", NodeTypeEnum.DataType.getName());
1172             return Either.right(operationStatus);
1173         }
1174         DataTypeData resultCTD = createDataTypeResult.left().value();
1175         List<PropertyDefinition> properties = dataTypeDefinition.getProperties();
1176         Either<Map<String, PropertyData>, JanusGraphOperationStatus> addPropertiesToDataType = addPropertiesToDataType(resultCTD.getUniqueId(),
1177             dataTypeDefinition.getModel(),
1178             properties);
1179         if (addPropertiesToDataType.isRight()) {
1180             log.debug("Failed add properties {} to data type {}", properties, dataTypeDefinition.getName());
1181             return Either.right(addPropertiesToDataType.right().value());
1182         }
1183
1184         final Either<GraphRelation, JanusGraphOperationStatus> modelRelationship = addDataTypeToModel(dataTypeDefinition);
1185         if (modelRelationship.isRight()) {
1186             return Either.right(modelRelationship.right().value());
1187         }
1188
1189         String derivedFrom = dataTypeDefinition.getDerivedFromName();
1190         if (derivedFrom != null) {
1191             final Either<DataTypeDefinition, JanusGraphOperationStatus> derivedFromDataType = getDataTypeByNameValidForModel(derivedFrom,
1192                 dataTypeDefinition.getModel());
1193             if (derivedFromDataType.isRight()) {
1194                 return Either.right(derivedFromDataType.right().value());
1195             }
1196
1197             log.debug("Before creating relation between data type {} to its parent {}", dtUniqueId, derivedFrom);
1198             UniqueIdData from = new UniqueIdData(NodeTypeEnum.DataType, dtUniqueId);
1199             final String deriveFromUid = derivedFromDataType.left().value().getUniqueId();
1200             UniqueIdData to = new UniqueIdData(NodeTypeEnum.DataType, deriveFromUid);
1201             Either<GraphRelation, JanusGraphOperationStatus> createRelation = janusGraphGenericDao
1202                 .createRelation(from, to, GraphEdgeLabels.DERIVED_FROM, null);
1203             log.debug("After create relation between capability type {} to its parent {}. status is {}", dtUniqueId, derivedFrom, createRelation);
1204             if (createRelation.isRight()) {
1205                 return Either.right(createRelation.right().value());
1206             }
1207         }
1208         return Either.left(createDataTypeResult.left().value());
1209     }
1210
1211     private Either<GraphRelation, JanusGraphOperationStatus> addDataTypeToModel(final DataTypeDefinition dataTypeDefinition) {
1212         final String model = dataTypeDefinition.getModel();
1213         if (model == null) {
1214             return Either.left(null);
1215         }
1216         final GraphNode from = new UniqueIdData(NodeTypeEnum.Model, UniqueIdBuilder.buildModelUid(model));
1217         final GraphNode to = new UniqueIdData(NodeTypeEnum.DataType, dataTypeDefinition.getUniqueId());
1218         log.info("Connecting model {} to type {}", from, to);
1219         return janusGraphGenericDao.createRelation(from, to, GraphEdgeLabels.MODEL_ELEMENT, Collections.emptyMap());
1220     }
1221
1222     private DataTypeData buildDataTypeData(DataTypeDefinition dataTypeDefinition, String ctUniqueId) {
1223         DataTypeData dataTypeData = new DataTypeData(dataTypeDefinition);
1224         dataTypeData.getDataTypeDataDefinition().setUniqueId(ctUniqueId);
1225         Long creationDate = dataTypeData.getDataTypeDataDefinition().getCreationTime();
1226         if (creationDate == null) {
1227             creationDate = System.currentTimeMillis();
1228         }
1229         dataTypeData.getDataTypeDataDefinition().setCreationTime(creationDate);
1230         dataTypeData.getDataTypeDataDefinition().setModificationTime(creationDate);
1231         return dataTypeData;
1232     }
1233
1234     /**
1235      * add properties to capability type.
1236      * <p>
1237      * Per property, add a property node and associate it to the capability type
1238      *
1239      * @param uniqueId
1240      * @param properties
1241      * @return
1242      */
1243     private Either<Map<String, PropertyData>, JanusGraphOperationStatus> addPropertiesToDataType(final String uniqueId, final String modelName,
1244                                                                                                  final List<PropertyDefinition> properties) {
1245         Map<String, PropertyData> propertiesData = new HashMap<>();
1246         if (properties != null && !properties.isEmpty()) {
1247             for (PropertyDefinition propertyDefinition : properties) {
1248                 String propertyName = propertyDefinition.getName();
1249                 String propertyType = propertyDefinition.getType();
1250                 Either<Boolean, JanusGraphOperationStatus> validPropertyType = isValidPropertyType(propertyType, modelName);
1251                 if (validPropertyType.isRight()) {
1252                     log.debug("Data type {} contains invalid property type {}", uniqueId, propertyType);
1253                     return Either.right(validPropertyType.right().value());
1254                 }
1255                 Boolean isValid = validPropertyType.left().value();
1256                 if (isValid == null || !isValid.booleanValue()) {
1257                     log.debug("Data type {} contains invalid property type {}", uniqueId, propertyType);
1258                     return Either.right(JanusGraphOperationStatus.INVALID_TYPE);
1259                 }
1260                 Either<PropertyData, JanusGraphOperationStatus> addPropertyToNodeType = this
1261                     .addPropertyToNodeType(propertyName, propertyDefinition, NodeTypeEnum.DataType, uniqueId);
1262                 if (addPropertyToNodeType.isRight()) {
1263                     JanusGraphOperationStatus operationStatus = addPropertyToNodeType.right().value();
1264                     log.debug("Failed to associate data type {} to property {} in graph. status is {}", uniqueId, propertyName, operationStatus);
1265                     BeEcompErrorManager.getInstance()
1266                         .logInternalFlowError("AddPropertyToDataType", "Failed to associate property to data type. Status is " + operationStatus,
1267                             ErrorSeverity.ERROR);
1268                     return Either.right(operationStatus);
1269                 }
1270                 propertiesData.put(propertyName, addPropertyToNodeType.left().value());
1271             }
1272             DataTypeData dataTypeData = new DataTypeData();
1273             dataTypeData.getDataTypeDataDefinition().setUniqueId(uniqueId);
1274             long modificationTime = System.currentTimeMillis();
1275             dataTypeData.getDataTypeDataDefinition().setModificationTime(modificationTime);
1276             Either<DataTypeData, JanusGraphOperationStatus> updateNode = janusGraphGenericDao.updateNode(dataTypeData, DataTypeData.class);
1277             if (updateNode.isRight()) {
1278                 JanusGraphOperationStatus operationStatus = updateNode.right().value();
1279                 log.debug("Failed to update modification time data type {} from graph. status is {}", uniqueId, operationStatus);
1280                 BeEcompErrorManager.getInstance()
1281                     .logInternalFlowError("AddPropertyToDataType", "Failed to fetch data type. Status is " + operationStatus, ErrorSeverity.ERROR);
1282                 return Either.right(operationStatus);
1283             } else {
1284                 log.debug("Update data type uid {}. Set modification time to {}", uniqueId, modificationTime);
1285             }
1286         }
1287         return Either.left(propertiesData);
1288     }
1289
1290     public Either<DataTypeDefinition, JanusGraphOperationStatus> getDataTypeByNameValidForModel(final String name, final String modelName) {
1291         final Either<DataTypeData, JanusGraphOperationStatus> dataTypesRes = janusGraphGenericDao
1292             .getNode(GraphPropertiesDictionary.NAME.getProperty(), name, DataTypeData.class, modelName);
1293         if (dataTypesRes.isRight()) {
1294             final JanusGraphOperationStatus status = dataTypesRes.right().value();
1295             log.debug(DATA_TYPE_CANNOT_BE_FOUND_IN_GRAPH_STATUS_IS, name, status);
1296             return Either.right(status);
1297         }
1298         final DataTypeData dataType = dataTypesRes.left().value();
1299         final DataTypeDefinition dataTypeDefinition = new DataTypeDefinition(dataType.getDataTypeDataDefinition());
1300         final JanusGraphOperationStatus propertiesStatus = fillProperties(dataTypeDefinition.getUniqueId(), dataTypeDefinition);
1301         if (propertiesStatus != JanusGraphOperationStatus.OK) {
1302             log.error(BUSINESS_PROCESS_ERROR, FAILED_TO_FETCH_PROPERTIES_OF_DATA_TYPE, dataTypeDefinition.getUniqueId());
1303             return Either.right(propertiesStatus);
1304         }
1305         final Either<ImmutablePair<DataTypeData, GraphEdge>, JanusGraphOperationStatus> parentNode = janusGraphGenericDao
1306             .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), dataTypeDefinition.getUniqueId(), GraphEdgeLabels.DERIVED_FROM,
1307                 NodeTypeEnum.DataType,
1308                 DataTypeData.class);
1309         log.debug(AFTER_RETRIEVING_DERIVED_FROM_NODE_OF_STATUS_IS, dataTypeDefinition.getUniqueId(), parentNode);
1310         if (parentNode.isRight()) {
1311             final JanusGraphOperationStatus janusGraphOperationStatus = parentNode.right().value();
1312             if (janusGraphOperationStatus != JanusGraphOperationStatus.NOT_FOUND) {
1313                 log.error(BUSINESS_PROCESS_ERROR, "Failed to find the parent data type of data type {}. status is {}",
1314                     dataTypeDefinition.getUniqueId(), janusGraphOperationStatus);
1315                 return Either.right(janusGraphOperationStatus);
1316             }
1317         } else {
1318             // derived from node was found
1319             final ImmutablePair<DataTypeData, GraphEdge> immutablePair = parentNode.left().value();
1320             final DataTypeData parentDataType = immutablePair.getKey();
1321             final Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeByUid = getDataTypeByUid(parentDataType.getUniqueId());
1322             if (dataTypeByUid.isRight()) {
1323                 return Either.right(dataTypeByUid.right().value());
1324             }
1325             DataTypeDefinition parentDataTypeDefinition = dataTypeByUid.left().value();
1326             dataTypeDefinition.setDerivedFrom(parentDataTypeDefinition);
1327         }
1328         return Either.left(dataTypeDefinition);
1329     }
1330
1331     /**
1332      * Build Data type object from graph by unique id
1333      *
1334      * @param uniqueId
1335      * @return
1336      */
1337     public Either<DataTypeDefinition, JanusGraphOperationStatus> getDataTypeByUid(String uniqueId) {
1338         Either<DataTypeDefinition, JanusGraphOperationStatus> result = null;
1339         Either<DataTypeData, JanusGraphOperationStatus> dataTypesRes = janusGraphGenericDao
1340             .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, DataTypeData.class);
1341         if (dataTypesRes.isRight()) {
1342             JanusGraphOperationStatus status = dataTypesRes.right().value();
1343             log.debug(DATA_TYPE_CANNOT_BE_FOUND_IN_GRAPH_STATUS_IS, uniqueId, status);
1344             return Either.right(status);
1345         }
1346         DataTypeData ctData = dataTypesRes.left().value();
1347         DataTypeDefinition dataTypeDefinition = new DataTypeDefinition(ctData.getDataTypeDataDefinition());
1348         JanusGraphOperationStatus propertiesStatus = fillProperties(uniqueId, dataTypeDefinition);
1349         if (propertiesStatus != JanusGraphOperationStatus.OK) {
1350             log.error(FAILED_TO_FETCH_PROPERTIES_OF_DATA_TYPE, uniqueId);
1351             return Either.right(propertiesStatus);
1352         }
1353         Either<ImmutablePair<DataTypeData, GraphEdge>, JanusGraphOperationStatus> parentNode = janusGraphGenericDao
1354             .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.DataType,
1355                 DataTypeData.class);
1356         log.debug(AFTER_RETRIEVING_DERIVED_FROM_NODE_OF_STATUS_IS, uniqueId, parentNode);
1357         if (parentNode.isRight()) {
1358             JanusGraphOperationStatus janusGraphOperationStatus = parentNode.right().value();
1359             if (janusGraphOperationStatus != JanusGraphOperationStatus.NOT_FOUND) {
1360                 log.error("Failed to find the parent data type of data type {}. status is {}", uniqueId, janusGraphOperationStatus);
1361                 result = Either.right(janusGraphOperationStatus);
1362                 return result;
1363             }
1364         } else {
1365             // derived from node was found
1366             ImmutablePair<DataTypeData, GraphEdge> immutablePair = parentNode.left().value();
1367             DataTypeData parentCT = immutablePair.getKey();
1368             String parentUniqueId = parentCT.getUniqueId();
1369             Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeByUid = getDataTypeByUid(parentUniqueId);
1370             if (dataTypeByUid.isRight()) {
1371                 return Either.right(dataTypeByUid.right().value());
1372             }
1373             DataTypeDefinition parentDataTypeDefinition = dataTypeByUid.left().value();
1374             dataTypeDefinition.setDerivedFrom(parentDataTypeDefinition);
1375         }
1376         result = Either.left(dataTypeDefinition);
1377         return result;
1378     }
1379
1380     private JanusGraphOperationStatus fillProperties(String uniqueId, DataTypeDefinition dataTypeDefinition) {
1381         Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesOfNode = this
1382             .findPropertiesOfNode(NodeTypeEnum.DataType, uniqueId);
1383         if (findPropertiesOfNode.isRight()) {
1384             JanusGraphOperationStatus janusGraphOperationStatus = findPropertiesOfNode.right().value();
1385             log.debug("After looking for properties of vertex {}. status is {}", uniqueId, janusGraphOperationStatus);
1386             if (JanusGraphOperationStatus.NOT_FOUND.equals(janusGraphOperationStatus)) {
1387                 return JanusGraphOperationStatus.OK;
1388             } else {
1389                 return janusGraphOperationStatus;
1390             }
1391         } else {
1392             Map<String, PropertyDefinition> properties = findPropertiesOfNode.left().value();
1393             if (properties != null && !properties.isEmpty()) {
1394                 List<PropertyDefinition> listOfProps = new ArrayList<>();
1395                 for (Entry<String, PropertyDefinition> entry : properties.entrySet()) {
1396                     String propName = entry.getKey();
1397                     PropertyDefinition propertyDefinition = entry.getValue();
1398                     PropertyDefinition newPropertyDefinition = new PropertyDefinition(propertyDefinition);
1399                     newPropertyDefinition.setName(propName);
1400                     listOfProps.add(newPropertyDefinition);
1401                 }
1402                 dataTypeDefinition.setProperties(listOfProps);
1403             }
1404             return JanusGraphOperationStatus.OK;
1405         }
1406     }
1407
1408     private Either<DataTypeDefinition, StorageOperationStatus> addDataType(DataTypeDefinition dataTypeDefinition, boolean inTransaction) {
1409         Either<DataTypeDefinition, StorageOperationStatus> result = null;
1410         try {
1411             Either<DataTypeData, JanusGraphOperationStatus> eitherStatus = addDataTypeToGraph(dataTypeDefinition);
1412             if (eitherStatus.isRight()) {
1413                 log.debug("Failed to add data type {} to Graph. status is {}", dataTypeDefinition, eitherStatus.right().value().name());
1414                 BeEcompErrorManager.getInstance().logBeFailedAddingNodeTypeError("AddDataType", "DataType");
1415                 result = Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(eitherStatus.right().value()));
1416                 return result;
1417             } else {
1418                 DataTypeData capabilityTypeData = eitherStatus.left().value();
1419                 DataTypeDefinition dataTypeDefResult = convertDTDataToDTDefinition(capabilityTypeData);
1420                 log.debug("The returned CapabilityTypeDefinition is {}", dataTypeDefResult);
1421                 result = Either.left(dataTypeDefResult);
1422                 return result;
1423             }
1424         } finally {
1425             if (!inTransaction) {
1426                 if (result == null || result.isRight()) {
1427                     log.error(GOING_TO_EXECUTE_ROLLBACK_ON_GRAPH);
1428                     janusGraphGenericDao.rollback();
1429                 } else {
1430                     log.debug(GOING_TO_EXECUTE_COMMIT_ON_GRAPH);
1431                     janusGraphGenericDao.commit();
1432                 }
1433             }
1434         }
1435     }
1436
1437     @Override
1438     public Either<DataTypeDefinition, StorageOperationStatus> addDataType(DataTypeDefinition dataTypeDefinition) {
1439         return addDataType(dataTypeDefinition, true);
1440     }
1441
1442     @Override
1443     public Either<DataTypeDefinition, StorageOperationStatus> getDataTypeByName(final String name, final String validForModel,
1444                                                                                 final boolean inTransaction) {
1445         Either<DataTypeDefinition, StorageOperationStatus> result = null;
1446         try {
1447             Either<DataTypeDefinition, JanusGraphOperationStatus> ctResult = this.getDataTypeByNameValidForModel(name, validForModel);
1448             if (ctResult.isRight()) {
1449                 JanusGraphOperationStatus status = ctResult.right().value();
1450                 if (status != JanusGraphOperationStatus.NOT_FOUND) {
1451                     log.error("Failed to retrieve information on capability type {} status is {}", name, status);
1452                 }
1453                 result = Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(ctResult.right().value()));
1454                 return result;
1455             }
1456             result = Either.left(ctResult.left().value());
1457             return result;
1458         } finally {
1459             if (!inTransaction) {
1460                 if (result == null || result.isRight()) {
1461                     log.error(GOING_TO_EXECUTE_ROLLBACK_ON_GRAPH);
1462                     janusGraphGenericDao.rollback();
1463                 } else {
1464                     log.debug(GOING_TO_EXECUTE_COMMIT_ON_GRAPH);
1465                     janusGraphGenericDao.commit();
1466                 }
1467             }
1468         }
1469     }
1470
1471     @Override
1472     public Either<DataTypeDefinition, StorageOperationStatus> getDataTypeByName(final String name, final String validForModel) {
1473         return getDataTypeByName(name, validForModel, true);
1474     }
1475
1476     public Either<DataTypeDefinition, StorageOperationStatus> getDataTypeByUidWithoutDerived(String uid, boolean inTransaction) {
1477         Either<DataTypeDefinition, StorageOperationStatus> result = null;
1478         try {
1479             Either<DataTypeDefinition, JanusGraphOperationStatus> ctResult = this.getDataTypeByUidWithoutDerivedDataTypes(uid);
1480             if (ctResult.isRight()) {
1481                 JanusGraphOperationStatus status = ctResult.right().value();
1482                 if (status != JanusGraphOperationStatus.NOT_FOUND) {
1483                     log.error(BUSINESS_PROCESS_ERROR, "Failed to retrieve information on data type {} status is {}", uid, status);
1484                 }
1485                 result = Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(ctResult.right().value()));
1486                 return result;
1487             }
1488             result = Either.left(ctResult.left().value());
1489             return result;
1490         } finally {
1491             if (!inTransaction) {
1492                 if (result == null || result.isRight()) {
1493                     log.error(GOING_TO_EXECUTE_ROLLBACK_ON_GRAPH);
1494                     janusGraphGenericDao.rollback();
1495                 } else {
1496                     log.debug(GOING_TO_EXECUTE_COMMIT_ON_GRAPH);
1497                     janusGraphGenericDao.commit();
1498                 }
1499             }
1500         }
1501     }
1502
1503     public Either<DataTypeDefinition, JanusGraphOperationStatus> getDataTypeByUidWithoutDerivedDataTypes(String uniqueId) {
1504         Either<DataTypeData, JanusGraphOperationStatus> dataTypesRes = janusGraphGenericDao
1505             .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, DataTypeData.class);
1506         if (dataTypesRes.isRight()) {
1507             JanusGraphOperationStatus status = dataTypesRes.right().value();
1508             log.debug(DATA_TYPE_CANNOT_BE_FOUND_IN_GRAPH_STATUS_IS, uniqueId, status);
1509             return Either.right(status);
1510         }
1511         DataTypeData ctData = dataTypesRes.left().value();
1512         DataTypeDefinition dataTypeDefinition = new DataTypeDefinition(ctData.getDataTypeDataDefinition());
1513         JanusGraphOperationStatus propertiesStatus = fillProperties(uniqueId, dataTypeDefinition);
1514         if (propertiesStatus != JanusGraphOperationStatus.OK) {
1515             log.error(FAILED_TO_FETCH_PROPERTIES_OF_DATA_TYPE, uniqueId);
1516             return Either.right(propertiesStatus);
1517         }
1518         return Either.left(dataTypeDefinition);
1519     }
1520
1521     /**
1522      * convert between graph Node object to Java object
1523      *
1524      * @param dataTypeData
1525      * @return
1526      */
1527     protected DataTypeDefinition convertDTDataToDTDefinition(DataTypeData dataTypeData) {
1528         log.debug("The object returned after create data type is {}", dataTypeData);
1529         return new DataTypeDefinition(dataTypeData.getDataTypeDataDefinition());
1530     }
1531
1532     private Either<Boolean, JanusGraphOperationStatus> isValidPropertyType(String propertyType, final String modelName) {
1533         if (propertyType == null || propertyType.isEmpty()) {
1534             return Either.left(false);
1535         }
1536         ToscaPropertyType toscaPropertyType = ToscaPropertyType.isValidType(propertyType);
1537         if (toscaPropertyType == null) {
1538             return isDefinedInDataTypes(propertyType, modelName);
1539         } else {
1540             return Either.left(true);
1541         }
1542     }
1543
1544     public Either<Boolean, JanusGraphOperationStatus> isDefinedInDataTypes(final String propertyType, final String modelName) {
1545         Either<DataTypeDefinition, JanusGraphOperationStatus> dataType = getDataTypeByNameValidForModel(propertyType, modelName);
1546         if (dataType.isRight()) {
1547             JanusGraphOperationStatus status = dataType.right().value();
1548             if (status == JanusGraphOperationStatus.NOT_FOUND) {
1549                 return Either.left(false);
1550             }
1551             return Either.right(status);
1552         }
1553         return Either.left(true);
1554     }
1555
1556     public Either<Map<String, Map<String, DataTypeDefinition>>, JanusGraphOperationStatus> getAllDataTypes() {
1557         final Map<String, Map<String, DataTypeDefinition>> dataTypes = new HashMap<>();
1558         Either<Map<String, Map<String, DataTypeDefinition>>, JanusGraphOperationStatus> result = Either.left(dataTypes);
1559         final Map<String, DataTypeDefinition> allDataTypesFound = new HashMap<>();
1560
1561         final Map<String, List<String>> dataTypeUidstoModels = dataTypeOperation.getAllDataTypeUidsToModels();
1562
1563         if (dataTypeUidstoModels != null) {
1564             log.trace("Number of data types to load is {}", dataTypeUidstoModels.size());
1565             for (Map.Entry<String, List<String>> entry : dataTypeUidstoModels.entrySet()) {
1566                 log.trace("Going to fetch data type with uid {}", entry.getKey());
1567                 Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeByUid = this
1568                     .getAndAddDataTypeByUid(entry.getKey(), allDataTypesFound);
1569                 if (dataTypeByUid.isRight()) {
1570                     JanusGraphOperationStatus status = dataTypeByUid.right().value();
1571                     if (status == JanusGraphOperationStatus.NOT_FOUND) {
1572                         status = JanusGraphOperationStatus.INVALID_ID;
1573                     }
1574                     return Either.right(status);
1575                 }
1576                 for (final String model : entry.getValue()) {
1577                     if (!dataTypes.containsKey(model)) {
1578                         dataTypes.put(model, new HashMap<String, DataTypeDefinition>());
1579                     }
1580                     DataTypeDefinition dataTypeDefinition = allDataTypesFound.get(entry.getKey());
1581                     dataTypes.get(model).put(dataTypeDefinition.getName(), dataTypeDefinition);
1582                 }
1583             }
1584
1585         }
1586         if (log.isTraceEnabled()) {
1587             if (result.isRight()) {
1588                 log.trace("After fetching all data types {}", result);
1589             } else {
1590                 Map<String, Map<String, DataTypeDefinition>> map = result.left().value();
1591                 if (map != null) {
1592                     String types = map.keySet().stream().collect(Collectors.joining(",", "[", "]"));
1593                     log.trace("After fetching all data types {} ", types);
1594                 }
1595             }
1596         }
1597         return result;
1598     }
1599
1600     /**
1601      * Build Data type object from graph by unique id
1602      *
1603      * @param uniqueId
1604      * @return
1605      */
1606     private Either<DataTypeDefinition, JanusGraphOperationStatus> getAndAddDataTypeByUid(String uniqueId,
1607                                                                                          Map<String, DataTypeDefinition> allDataTypes) {
1608         Either<DataTypeDefinition, JanusGraphOperationStatus> result = null;
1609         if (allDataTypes.containsKey(uniqueId)) {
1610             return Either.left(allDataTypes.get(uniqueId));
1611         }
1612         Either<DataTypeData, JanusGraphOperationStatus> dataTypesRes = janusGraphGenericDao
1613             .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, DataTypeData.class);
1614         if (dataTypesRes.isRight()) {
1615             JanusGraphOperationStatus status = dataTypesRes.right().value();
1616             log.debug(DATA_TYPE_CANNOT_BE_FOUND_IN_GRAPH_STATUS_IS, uniqueId, status);
1617             return Either.right(status);
1618         }
1619         DataTypeData ctData = dataTypesRes.left().value();
1620         DataTypeDefinition dataTypeDefinition = new DataTypeDefinition(ctData.getDataTypeDataDefinition());
1621         JanusGraphOperationStatus propertiesStatus = fillProperties(uniqueId, dataTypeDefinition);
1622         if (propertiesStatus != JanusGraphOperationStatus.OK) {
1623             log.error(FAILED_TO_FETCH_PROPERTIES_OF_DATA_TYPE, uniqueId);
1624             return Either.right(propertiesStatus);
1625         }
1626         allDataTypes.put(dataTypeDefinition.getUniqueId(), dataTypeDefinition);
1627         String derivedFrom = dataTypeDefinition.getDerivedFromName();
1628         if (allDataTypes.containsKey(derivedFrom)) {
1629             DataTypeDefinition parentDataTypeDefinition = allDataTypes.get(derivedFrom);
1630             dataTypeDefinition.setDerivedFrom(parentDataTypeDefinition);
1631             return Either.left(dataTypeDefinition);
1632         }
1633         Either<ImmutablePair<DataTypeData, GraphEdge>, JanusGraphOperationStatus> parentNode = janusGraphGenericDao
1634             .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.DataType,
1635                 DataTypeData.class);
1636         log.debug(AFTER_RETRIEVING_DERIVED_FROM_NODE_OF_STATUS_IS, uniqueId, parentNode);
1637         if (parentNode.isRight()) {
1638             JanusGraphOperationStatus janusGraphOperationStatus = parentNode.right().value();
1639             if (janusGraphOperationStatus != JanusGraphOperationStatus.NOT_FOUND) {
1640                 log.error("Failed to find the parent data type of data type {}. status is {}", uniqueId, janusGraphOperationStatus);
1641                 result = Either.right(janusGraphOperationStatus);
1642                 return result;
1643             }
1644         } else {
1645             // derived from node was found
1646             ImmutablePair<DataTypeData, GraphEdge> immutablePair = parentNode.left().value();
1647             DataTypeData parentCT = immutablePair.getKey();
1648             String parentUniqueId = parentCT.getUniqueId();
1649             Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeByUid = getDataTypeByUid(parentUniqueId);
1650             if (dataTypeByUid.isRight()) {
1651                 return Either.right(dataTypeByUid.right().value());
1652             }
1653             DataTypeDefinition parentDataTypeDefinition = dataTypeByUid.left().value();
1654             dataTypeDefinition.setDerivedFrom(parentDataTypeDefinition);
1655             final var model = getModel(uniqueId);
1656             if (StringUtils.isNotEmpty(model)) {
1657                 dataTypeDefinition.setModel(model);
1658             }
1659         }
1660         result = Either.left(dataTypeDefinition);
1661         return result;
1662     }
1663
1664     private String getModel(final String uniqueId) {
1665         final Either<ImmutablePair<ModelData, GraphEdge>, JanusGraphOperationStatus> model = janusGraphGenericDao.getParentNode(
1666             UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, GraphEdgeLabels.MODEL_ELEMENT,
1667             NodeTypeEnum.Model, ModelData.class);
1668         return model.isLeft() ? model.left().value().getLeft().getName() : StringUtils.EMPTY;
1669     }
1670
1671     public Either<String, JanusGraphOperationStatus> checkInnerType(PropertyDataDefinition propDataDef) {
1672         String propertyType = propDataDef.getType();
1673         ToscaPropertyType type = ToscaPropertyType.isValidType(propertyType);
1674         return getInnerType(type, propDataDef::getSchema);
1675     }
1676
1677     public Either<Object, Boolean> validateAndUpdatePropertyValue(String propertyType, String value, boolean isValidate, String innerType,
1678                                                                   Map<String, DataTypeDefinition> dataTypes) {
1679         log.trace("Going to validate property value and its type. type = {}, value = {}", propertyType, value);
1680         final ToscaPropertyType type = getType(propertyType);
1681         if (isValidate) {
1682             if (type == null) {
1683                 DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
1684                 ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter
1685                     .validateAndUpdate(value, dataTypeDefinition, dataTypes);
1686                 if (Boolean.FALSE.equals(validateResult.right)) {
1687                     log.debug(THE_VALUE_OF_PROPERTY_FROM_TYPE_IS_INVALID, value, propertyType);
1688                     return Either.right(false);
1689                 }
1690                 JsonElement jsonElement = validateResult.left;
1691                 String valueFromJsonElement = getValueFromJsonElement(jsonElement);
1692                 return Either.left(valueFromJsonElement);
1693             }
1694             log.trace("before validating property type {}", propertyType);
1695             boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
1696             if (!isValidProperty) {
1697                 log.debug(THE_VALUE_OF_PROPERTY_FROM_TYPE_IS_INVALID, value, type);
1698                 return Either.right(false);
1699             }
1700         }
1701         Object convertedValue = value;
1702         if (!isEmptyValue(value) && isValidate) {
1703             PropertyValueConverter converter = type.getConverter();
1704             convertedValue = converter.convert(value, innerType, dataTypes);
1705         }
1706         return Either.left(convertedValue);
1707     }
1708
1709     public Either<Object, Boolean> validateAndUpdatePropertyValue(String propertyType, String value, String innerType,
1710                                                                   Map<String, DataTypeDefinition> dataTypes) {
1711         return validateAndUpdatePropertyValue(propertyType, value, true, innerType, dataTypes);
1712     }
1713
1714     public Either<Object, Boolean> validateAndUpdatePropertyValue(final Component containerComponent, final PropertyDataDefinition property,
1715                                                                   final Map<String, DataTypeDefinition> dataTypes) {
1716         if (property.isToscaFunction()) {
1717             toscaFunctionValidator.validate(property, containerComponent);
1718             property.setValue(property.getToscaFunction().getValue());
1719             return Either.left(property.getValue());
1720         }
1721         Either<String, JanusGraphOperationStatus> checkInnerType = checkInnerType(property);
1722         if (checkInnerType.isRight()) {
1723             return Either.right(false);
1724         }
1725         final String innerType = checkInnerType.left().value();
1726         return validateAndUpdatePropertyValue(property.getType(), property.getValue(), true, innerType, dataTypes);
1727     }
1728
1729     public <T extends GraphNode> Either<List<PropertyDefinition>, StorageOperationStatus> getAllPropertiesRec(String uniqueId, NodeTypeEnum nodeType,
1730                                                                                                               Class<T> clazz) {
1731         return this.findPropertiesOfNode(nodeType, uniqueId).right().bind(this::handleNotFoundProperties).left()
1732             .bind(props -> getAllDerivedFromChainProperties(uniqueId, nodeType, clazz, props.values()));
1733     }
1734
1735     private Either<Map<String, PropertyDefinition>, StorageOperationStatus> handleNotFoundProperties(
1736         JanusGraphOperationStatus janusGraphOperationStatus) {
1737         if (janusGraphOperationStatus == JanusGraphOperationStatus.NOT_FOUND) {
1738             return Either.left(new HashMap<>());
1739         }
1740         return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(janusGraphOperationStatus));
1741     }
1742
1743     private <T extends GraphNode> Either<List<PropertyDefinition>, StorageOperationStatus> getAllDerivedFromChainProperties(String uniqueId,
1744                                                                                                                             NodeTypeEnum nodeType,
1745                                                                                                                             Class<T> clazz,
1746                                                                                                                             Collection<PropertyDefinition> nodeProps) {
1747         List<PropertyDefinition> accumulatedProps = new ArrayList<>(nodeProps);
1748         String currentNodeUid = uniqueId;
1749         Either<T, StorageOperationStatus> derivedFrom;
1750         while ((derivedFrom = derivedFromOperation.getDerivedFromChild(currentNodeUid, nodeType, clazz)).isLeft()) {
1751             currentNodeUid = derivedFrom.left().value().getUniqueId();
1752             JanusGraphOperationStatus janusGraphOperationStatus = fillPropertiesList(currentNodeUid, nodeType, accumulatedProps::addAll);
1753             if (janusGraphOperationStatus != JanusGraphOperationStatus.OK) {
1754                 log.debug("failed to fetch properties for type {} with id {}", nodeType, currentNodeUid);
1755                 return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(janusGraphOperationStatus));
1756             }
1757         }
1758         StorageOperationStatus getDerivedResult = derivedFrom.right().value();
1759         return isReachedEndOfDerivedFromChain(getDerivedResult) ? Either.left(accumulatedProps) : Either.right(getDerivedResult);
1760     }
1761
1762     private boolean isReachedEndOfDerivedFromChain(StorageOperationStatus getDerivedResult) {
1763         return getDerivedResult == StorageOperationStatus.NOT_FOUND;
1764     }
1765
1766     /*
1767      * @Override public PropertyOperation getPropertyOperation() { return this; }
1768      */
1769     public JanusGraphOperationStatus fillPropertiesList(String uniqueId, NodeTypeEnum nodeType, Consumer<List<PropertyDefinition>> propertySetter) {
1770         Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesRes = findPropertiesifExist(uniqueId, nodeType);
1771         if (findPropertiesRes.isRight()) {
1772             return findPropertiesRes.right().value();
1773         }
1774         Map<String, PropertyDefinition> properties = findPropertiesRes.left().value();
1775         if (properties != null) {
1776             List<PropertyDefinition> propertiesAsList = properties.entrySet().stream().map(Entry::getValue).collect(Collectors.toList());
1777             propertySetter.accept(propertiesAsList);
1778         }
1779         return JanusGraphOperationStatus.OK;
1780     }
1781
1782     Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesifExist(String uniqueId, NodeTypeEnum nodeType) {
1783         Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesOfNode = this.findPropertiesOfNode(nodeType, uniqueId);
1784         if (findPropertiesOfNode.isRight()) {
1785             log.debug("After looking for properties of vertex {}. status is {}", uniqueId, findPropertiesOfNode.right().value());
1786             if (findPropertiesOfNode.right().value() == JanusGraphOperationStatus.NOT_FOUND) {
1787                 return Either.left(Maps.newHashMap());
1788             }
1789             return findPropertiesOfNode;
1790         }
1791         return findPropertiesOfNode;
1792     }
1793
1794     /**
1795      * add properties to element type.
1796      * <p>
1797      * Per property, add a property node and associate it to the element type
1798      *
1799      * @param uniqueId
1800      * @param propertiesMap
1801      * @return
1802      */
1803     protected Either<Map<String, PropertyData>, JanusGraphOperationStatus> addPropertiesToElementType(String uniqueId, NodeTypeEnum nodeType,
1804                                                                                                       Map<String, PropertyDefinition> propertiesMap) {
1805         Map<String, PropertyData> propertiesData = new HashMap<>();
1806         if (propertiesMap != null) {
1807             for (Entry<String, PropertyDefinition> propertyDefinitionEntry : propertiesMap.entrySet()) {
1808                 String propertyName = propertyDefinitionEntry.getKey();
1809                 Either<PropertyData, JanusGraphOperationStatus> addPropertyToNodeType = this
1810                     .addPropertyToNodeType(propertyName, propertyDefinitionEntry.getValue(), nodeType, uniqueId);
1811                 if (addPropertyToNodeType.isRight()) {
1812                     JanusGraphOperationStatus operationStatus = addPropertyToNodeType.right().value();
1813                     log.error("Failed to associate {} {} to property {} in graph. status is {}", nodeType.getName(), uniqueId, propertyName,
1814                         operationStatus);
1815                     return Either.right(operationStatus);
1816                 }
1817                 propertiesData.put(propertyName, addPropertyToNodeType.left().value());
1818             }
1819         }
1820         return Either.left(propertiesData);
1821     }
1822
1823     public Either<Map<String, PropertyData>, JanusGraphOperationStatus> addPropertiesToElementType(String uniqueId, NodeTypeEnum elementType,
1824                                                                                                    List<PropertyDefinition> properties) {
1825         Map<String, PropertyDefinition> propMap;
1826         if (properties == null) {
1827             propMap = null;
1828         } else {
1829             propMap = properties.stream().collect(Collectors.toMap(PropertyDataDefinition::getName, propDef -> propDef));
1830         }
1831         return addPropertiesToElementType(uniqueId, elementType, propMap);
1832     }
1833
1834     @Override
1835     public Either<DataTypeDefinition, StorageOperationStatus> updateDataType(DataTypeDefinition newDataTypeDefinition,
1836                                                                              DataTypeDefinition oldDataTypeDefinition) {
1837         return updateDataType(newDataTypeDefinition, oldDataTypeDefinition, true);
1838     }
1839
1840     private Either<DataTypeDefinition, StorageOperationStatus> updateDataType(DataTypeDefinition newDataTypeDefinition,
1841                                                                               DataTypeDefinition oldDataTypeDefinition, boolean inTransaction) {
1842         Either<DataTypeDefinition, StorageOperationStatus> result = null;
1843         try {
1844             List<PropertyDefinition> newProperties = newDataTypeDefinition.getProperties();
1845             List<PropertyDefinition> oldProperties = oldDataTypeDefinition.getProperties();
1846             String newDerivedFromName = newDataTypeDefinition.getDerivedFromName();
1847             String oldDerivedFromName = oldDataTypeDefinition.getDerivedFromName();
1848             String dataTypeName = newDataTypeDefinition.getName();
1849             List<PropertyDefinition> propertiesToAdd = new ArrayList<>();
1850             if (isPropertyTypeChanged(dataTypeName, newProperties, oldProperties, propertiesToAdd)
1851                 || isDerivedFromNameChanged(dataTypeName, newDerivedFromName, oldDerivedFromName)) {
1852                 log.debug("The new data type {} is invalid.", dataTypeName);
1853                 result = Either.right(StorageOperationStatus.CANNOT_UPDATE_EXISTING_ENTITY);
1854                 return result;
1855             }
1856             if (CollectionUtils.isEmpty(propertiesToAdd)) {
1857                 log.debug("No new properties has been defined in the new data type {}", newDataTypeDefinition);
1858                 result = Either.right(StorageOperationStatus.OK);
1859                 return result;
1860             }
1861             Map<String, String> newDescriptions = getPropertyDescriptionsToUpdate(oldProperties, newProperties);
1862             if (MapUtils.isNotEmpty(newDescriptions)) {
1863                 JanusGraphOperationStatus updatePropertiesStatus = updateDataTypePropertyDescriptions(oldDataTypeDefinition.getUniqueId(),
1864                     newDescriptions);
1865                 if (updatePropertiesStatus != JanusGraphOperationStatus.OK) {
1866                     log.debug("#updateDataType - Failed to update the descriptions of the properties of the data type {}. Status is {}",
1867                         oldDataTypeDefinition, updatePropertiesStatus);
1868                     BeEcompErrorManager.getInstance().logBeFailedAddingNodeTypeError(UPDATE_DATA_TYPE, PROPERTY);
1869                     result = Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(updatePropertiesStatus));
1870                     return result;
1871                 }
1872             }
1873             Either<Map<String, PropertyData>, JanusGraphOperationStatus> addPropertiesToDataType = addPropertiesToDataType(
1874                 oldDataTypeDefinition.getUniqueId(), oldDataTypeDefinition.getModel(), propertiesToAdd);
1875             if (addPropertiesToDataType.isRight()) {
1876                 log.debug("Failed to update data type {} to Graph. Status is {}", oldDataTypeDefinition,
1877                     addPropertiesToDataType.right().value().name());
1878                 BeEcompErrorManager.getInstance().logBeFailedAddingNodeTypeError(UPDATE_DATA_TYPE, PROPERTY);
1879                 result = Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(addPropertiesToDataType.right().value()));
1880                 return result;
1881             } else {
1882                 Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeByUid = this.getDataTypeByUid(oldDataTypeDefinition.getUniqueId());
1883                 if (dataTypeByUid.isRight()) {
1884                     JanusGraphOperationStatus status = addPropertiesToDataType.right().value();
1885                     log.debug("Failed to get data type {} after update. Status is {}", oldDataTypeDefinition.getUniqueId(), status.name());
1886                     BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError(UPDATE_DATA_TYPE, PROPERTY, status.name());
1887                     result = Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(status));
1888                 } else {
1889                     result = Either.left(dataTypeByUid.left().value());
1890                 }
1891             }
1892             return result;
1893         } finally {
1894             if (!inTransaction) {
1895                 if (result == null || result.isRight()) {
1896                     log.error(GOING_TO_EXECUTE_ROLLBACK_ON_GRAPH);
1897                     janusGraphGenericDao.rollback();
1898                 } else {
1899                     log.debug(GOING_TO_EXECUTE_COMMIT_ON_GRAPH);
1900                     janusGraphGenericDao.commit();
1901                 }
1902             }
1903         }
1904     }
1905
1906     private boolean isPropertyTypeChanged(String dataTypeName, List<PropertyDefinition> newProperties, List<PropertyDefinition> oldProperties,
1907                                           List<PropertyDefinition> outputPropertiesToAdd) {
1908         if (newProperties != null && oldProperties != null) {
1909             Map<String, PropertyDefinition> newPropsMapper = newProperties.stream()
1910                 .collect(Collectors.toMap(PropertyDataDefinition::getName, p -> p));
1911             Map<String, PropertyDefinition> oldPropsMapper = oldProperties.stream()
1912                 .collect(Collectors.toMap(PropertyDataDefinition::getName, p -> p));
1913             for (Entry<String, PropertyDefinition> newPropertyEntry : newPropsMapper.entrySet()) {
1914                 String propName = newPropertyEntry.getKey();
1915                 PropertyDefinition propDef = newPropertyEntry.getValue();
1916                 PropertyDefinition oldPropertyDefinition = oldPropsMapper.get(propName);
1917                 if (oldPropertyDefinition == null) {
1918                     log.debug("New property {} received in the data type {}", propName, dataTypeName);
1919                     outputPropertiesToAdd.add(propDef);
1920                     continue;
1921                 }
1922                 String oldType = oldPropertyDefinition.getType();
1923                 String oldEntryType = getEntryType(oldPropertyDefinition);
1924                 String newType = propDef.getType();
1925                 String newEntryType = getEntryType(propDef);
1926                 if (!oldType.equals(newType)) {
1927                     log.debug("Existing property {} in data type {} has a differnet type {} than the new one {}", propName, dataTypeName, oldType,
1928                         newType);
1929                     return true;
1930                 }
1931                 if (!equalsEntryTypes(oldEntryType, newEntryType)) {
1932                     log.debug("Existing property {} in data type {} has a differnet entry type {} than the new one {}", propName, dataTypeName,
1933                         oldEntryType, newEntryType);
1934                     return true;
1935                 }
1936             }
1937         }
1938         return false;
1939     }
1940
1941     private boolean equalsEntryTypes(String oldEntryType, String newEntryType) {
1942         if (oldEntryType == null && newEntryType == null) {
1943             return true;
1944         } else if (oldEntryType != null && newEntryType != null) {
1945             return oldEntryType.equals(newEntryType);
1946         } else {
1947             return false;
1948         }
1949     }
1950
1951     private String getEntryType(PropertyDefinition oldPropertyDefinition) {
1952         String entryType = null;
1953         SchemaDefinition schema = oldPropertyDefinition.getSchema();
1954         if (schema != null) {
1955             PropertyDataDefinition schemaProperty = schema.getProperty();
1956             if (schemaProperty != null) {
1957                 entryType = schemaProperty.getType();
1958             }
1959         }
1960         return entryType;
1961     }
1962
1963     private boolean isDerivedFromNameChanged(String dataTypeName, String newDerivedFromName, String oldDerivedFromName) {
1964         if (newDerivedFromName != null) {
1965             boolean isEqual = newDerivedFromName.equals(oldDerivedFromName);
1966             if (!isEqual) {
1967                 log.debug("The new datatype {} derived from another data type {} than the existing one {}", dataTypeName, newDerivedFromName,
1968                     oldDerivedFromName);
1969             }
1970             return !isEqual;
1971         } else if (oldDerivedFromName == null) {
1972             return false;
1973         } else {// new=null, old != null
1974             log.debug("The new datatype {} derived from another data type {} than the existing one {}", dataTypeName, newDerivedFromName,
1975                 oldDerivedFromName);
1976             return true;
1977         }
1978     }
1979
1980     /**
1981      * @param instanceId
1982      * @param nodeType
1983      * @return
1984      */
1985     public Either<Integer, StorageOperationStatus> increaseAndGetObjInstancePropertyCounter(String instanceId, NodeTypeEnum nodeType) {
1986         Either<JanusGraph, JanusGraphOperationStatus> graphResult = janusGraphGenericDao.getGraph();
1987         if (graphResult.isRight()) {
1988             return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(graphResult.right().value()));
1989         }
1990         Either<JanusGraphVertex, JanusGraphOperationStatus> vertexService = janusGraphGenericDao
1991             .getVertexByProperty(UniqueIdBuilder.getKeyByNodeType(nodeType), instanceId);
1992         if (vertexService.isRight()) {
1993             log.debug("failed to fetch vertex of resource instance for id = {}", instanceId);
1994             return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(vertexService.right().value()));
1995         }
1996         Vertex vertex = vertexService.left().value();
1997         VertexProperty<Object> vertexProperty = vertex.property(GraphPropertiesDictionary.PROPERTY_COUNTER.getProperty());
1998         Integer counter = 0;
1999         if (vertexProperty.isPresent() && vertexProperty.value() != null) {
2000             counter = (Integer) vertexProperty.value();
2001         }
2002         counter++;
2003         vertex.property(GraphPropertiesDictionary.PROPERTY_COUNTER.getProperty(), counter);
2004         return Either.left(counter);
2005     }
2006
2007     public Either<List<PropertyDefinition>, JanusGraphOperationStatus> validatePropertiesUniqueness(
2008         Map<String, PropertyDefinition> inheritedProperties, List<PropertyDefinition> properties) {
2009         Either<List<PropertyDefinition>, JanusGraphOperationStatus> result = Either.left(properties);
2010         for (PropertyDefinition property : properties) {
2011             JanusGraphOperationStatus status = validatePropertyUniqueness(inheritedProperties, property);
2012             if (status != JanusGraphOperationStatus.OK) {
2013                 result = Either.right(status);
2014                 break;
2015             }
2016         }
2017         return result;
2018     }
2019
2020     /**
2021      * Validates uniqueness of examined property by comparing it with properties in propertiesOfType and updates if need type and inner type of the
2022      * property.
2023      */
2024     private JanusGraphOperationStatus validatePropertyUniqueness(Map<String, PropertyDefinition> inheritedProperties, PropertyDefinition property) {
2025         String propertyName = property.getName();
2026         String propertyType = property.getType();
2027         JanusGraphOperationStatus result = JanusGraphOperationStatus.OK;
2028         if (inheritedProperties.containsKey(propertyName)) {
2029             PropertyDefinition defaultProperty = inheritedProperties.get(propertyName);
2030             if (typesMismatch(propertyType, defaultProperty.getType())) {
2031                 log.error("#validatePropertyUniqueness - Property with name {} and different type already exists.", propertyName);
2032                 result = JanusGraphOperationStatus.PROPERTY_NAME_ALREADY_EXISTS;
2033             } else {
2034                 property.setType(defaultProperty.getType());
2035                 String innerType = defaultProperty.getSchemaType();
2036                 PropertyDataDefinition schemaProperty = property.getSchemaProperty();
2037                 if (schemaProperty != null) {
2038                     schemaProperty.setType(innerType);
2039                 }
2040             }
2041         }
2042         return result;
2043     }
2044
2045     private boolean typesMismatch(String type1, String type2) {
2046         return type1 != null && type2 != null && !type2.equals(type1);
2047     }
2048
2049     public <T extends GraphNode> Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> getAllTypePropertiesFromAllDerivedFrom(
2050         String nextParentUid, NodeTypeEnum nodeType, Class<T> clazz) {
2051         Map<String, PropertyDefinition> allProperies = new HashMap<>();
2052         return getTypePropertiesFromDerivedFromRecursively(nextParentUid, allProperies, nodeType, clazz);
2053     }
2054
2055     private <T extends GraphNode> Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> getTypePropertiesFromDerivedFromRecursively(
2056         String nextParentUid, Map<String, PropertyDefinition> allProperies, NodeTypeEnum nodeType, Class<T> clazz) {
2057         JanusGraphOperationStatus error;
2058         Either<List<ImmutablePair<T, GraphEdge>>, JanusGraphOperationStatus> childrenNodes = janusGraphGenericDao
2059             .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), nextParentUid, GraphEdgeLabels.DERIVED_FROM, nodeType, clazz);
2060         if (childrenNodes.isRight()) {
2061             if (childrenNodes.right().value() != JanusGraphOperationStatus.NOT_FOUND) {
2062                 error = childrenNodes.right().value();
2063                 log.debug("#getTypePropertiesFromDerivedFromRecursively - Couldn't fetch derived from node with UID {}, error: {}", nextParentUid,
2064                     error);
2065                 return Either.right(error);
2066             } else {
2067                 log.debug("#getTypePropertiesFromDerivedFromRecursively - Derived from node is not found with UID {} - this is OK for root.",
2068                     nextParentUid);
2069                 return Either.left(allProperies);
2070             }
2071         } else {
2072             Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> allPropertiesOfTypeRes = findPropertiesOfNode(nodeType, nextParentUid);
2073             if (allPropertiesOfTypeRes.isRight() && !allPropertiesOfTypeRes.right().value().equals(JanusGraphOperationStatus.NOT_FOUND)) {
2074                 error = allPropertiesOfTypeRes.right().value();
2075                 log.error(
2076                     "#getTypePropertiesFromDerivedFromRecursively - Failed to retrieve properties for node with UID {} from graph. status is {}",
2077                     nextParentUid, error);
2078                 return Either.right(error);
2079             } else if (allPropertiesOfTypeRes.isLeft()) {
2080                 if (allProperies.isEmpty()) {
2081                     allProperies.putAll(allPropertiesOfTypeRes.left().value());
2082                 } else {
2083                     allProperies.putAll(allPropertiesOfTypeRes.left().value().entrySet().stream().filter(e -> !allProperies.containsKey(e.getKey()))
2084                         .collect(Collectors.toMap(Entry::getKey, Entry::getValue)));
2085                 }
2086             }
2087             return getTypePropertiesFromDerivedFromRecursively(childrenNodes.left().value().get(0).getLeft().getUniqueId(), allProperies, nodeType,
2088                 clazz);
2089         }
2090     }
2091
2092     private JanusGraphOperationStatus updateDataTypePropertyDescriptions(String uniqueId, Map<String, String> newDescriptions) {
2093         if (MapUtils.isNotEmpty(newDescriptions)) {
2094             Either<List<ImmutablePair<JanusGraphVertex, Edge>>, JanusGraphOperationStatus> getDataTypePropertiesRes = janusGraphGenericDao
2095                 .getChildrenVertecies(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), uniqueId, GraphEdgeLabels.PROPERTY);
2096             if (getDataTypePropertiesRes.isRight()) {
2097                 log.debug("#updateDataTypePropertiesDescriptions - Failed to fetch the property verticies of the Data type {} ", uniqueId);
2098                 return getDataTypePropertiesRes.right().value();
2099             }
2100             getDataTypePropertiesRes.left().value().stream().filter(pair -> newDescriptions.containsKey(getPropertyNameFromEdge(pair)))
2101                 .forEach(pair -> setNewDescriptionToVertex(newDescriptions.get(getPropertyNameFromEdge(pair)), pair));
2102         }
2103         return JanusGraphOperationStatus.OK;
2104     }
2105
2106     private JanusGraphVertexProperty<String> setNewDescriptionToVertex(String newDescription, ImmutablePair<JanusGraphVertex, Edge> pair) {
2107         return pair.getLeft().property(GraphPropertiesDictionary.DESCRIPTION.getProperty(), newDescription);
2108     }
2109
2110     private String getPropertyNameFromEdge(ImmutablePair<JanusGraphVertex, Edge> pair) {
2111         return (String) pair.getRight().property(GraphPropertiesDictionary.NAME.getProperty()).value();
2112     }
2113
2114     private Map<String, String> getPropertyDescriptionsToUpdate(List<PropertyDefinition> oldProperties, List<PropertyDefinition> newProperties) {
2115         Map<String, PropertyDefinition> newPropertiesMap = newProperties.stream().collect(Collectors.toMap(PropertyDefinition::getName, p -> p));
2116         return oldProperties.stream()
2117             .filter(p -> newPropertiesMap.containsKey(p.getName()) && !descriptionsEqual(p, newPropertiesMap.get(p.getName())))
2118             .collect(Collectors.toMap(PropertyDefinition::getName, p -> newPropertiesMap.get(p.getName()).getDescription()));
2119     }
2120
2121     private boolean descriptionsEqual(PropertyDefinition property, PropertyDefinition otherProperty) {
2122         if (StringUtils.isEmpty(property.getDescription()) && StringUtils.isEmpty(otherProperty.getDescription())) {
2123             return true;
2124         }
2125         if (StringUtils.isNotEmpty(property.getDescription()) && StringUtils.isEmpty(otherProperty.getDescription())) {
2126             return false;
2127         }
2128         if (StringUtils.isEmpty(property.getDescription()) && StringUtils.isNotEmpty(otherProperty.getDescription())) {
2129             return false;
2130         }
2131         return property.getDescription().equals(otherProperty.getDescription());
2132     }
2133
2134     public static class PropertyConstraintSerialiser implements JsonSerializer<PropertyConstraint> {
2135
2136         @Override
2137         public JsonElement serialize(PropertyConstraint src, Type typeOfSrc, JsonSerializationContext context) {
2138             JsonObject result = new JsonObject();
2139             JsonArray jsonArray = new JsonArray();
2140             if (src instanceof InRangeConstraint) {
2141                 InRangeConstraint rangeConstraint = (InRangeConstraint) src;
2142                 jsonArray.add(JsonParser.parseString(String.valueOf(rangeConstraint.getRangeMinValue())));
2143                 jsonArray.add(JsonParser.parseString(String.valueOf(rangeConstraint.getRangeMaxValue())));
2144                 result.add("inRange", jsonArray);
2145             } else if (src instanceof GreaterThanConstraint) {
2146                 GreaterThanConstraint greaterThanConstraint = (GreaterThanConstraint) src;
2147                 jsonArray.add(JsonParser.parseString(String.valueOf(greaterThanConstraint.getGreaterThan())));
2148                 result.add("greaterThan", jsonArray);
2149             } else if (src instanceof LessThanConstraint) {
2150                 LessThanConstraint lessThanConstraint = (LessThanConstraint) src;
2151                 jsonArray.add(JsonParser.parseString(String.valueOf(lessThanConstraint.getLessThan())));
2152                 result.add("lessThan", jsonArray);
2153             } else if (src instanceof LessOrEqualConstraint) {
2154                 LessOrEqualConstraint lessOrEqualConstraint = (LessOrEqualConstraint) src;
2155                 jsonArray.add(JsonParser.parseString(String.valueOf(lessOrEqualConstraint.getLessOrEqual())));
2156                 result.add("lessOrEqual", jsonArray);
2157             } else {
2158                 log.warn("PropertyConstraint {} is not supported. Ignored.", src.getClass().getName());
2159             }
2160             return result;
2161         }
2162     }
2163
2164     public static class PropertyConstraintDeserialiser implements JsonDeserializer<PropertyConstraint> {
2165
2166         private static final String THE_VALUE_OF_GREATER_THAN_CONSTRAINT_IS_NULL = "The value of GreaterThanConstraint is null";
2167
2168         @Override
2169         public PropertyConstraint deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
2170             PropertyConstraint propertyConstraint = null;
2171             final Set<Entry<String, JsonElement>> set = json.getAsJsonObject().entrySet();
2172             if (!set.isEmpty()) {
2173                 final Entry<String, JsonElement> element = set.iterator().next();
2174                 final String key = element.getKey();
2175                 final ConstraintType constraintType = ConstraintType.findByType(key).orElse(null);
2176                 if (constraintType == null) {
2177                     log.warn("ConstraintType was not found for constraint name:{}", key);
2178                 } else {
2179                     if (set.size() == 1 || (set.size() == 2 && ConstraintType.PATTERN == constraintType)) {
2180                         final JsonElement value = element.getValue();
2181                         final Object typedValue = getTypedValue(value);
2182                         switch (constraintType) {
2183                             case EQUAL:
2184                                 if (typedValue != null) {
2185                                     log.debug("Before adding value to EqualConstraint object. value = {}", typedValue);
2186                                     propertyConstraint = new EqualConstraint(typedValue);
2187                                     break;
2188                                 } else {
2189                                     log.warn("The value of equal constraint is null");
2190                                 }
2191                                 break;
2192                             case IN_RANGE:
2193                                 if (typedValue != null) {
2194                                     if (typedValue instanceof ArrayList) {
2195                                         ArrayList rangeArray = (ArrayList) typedValue;
2196                                         if (rangeArray.size() != 2 || rangeArray.contains("")) {
2197                                             log.error("The range constraint content is invalid. value = {}", typedValue);
2198                                             throw new JsonSyntaxException("The range constraint content is invalid");
2199                                         } else {
2200                                             InRangeConstraint rangeConstraint = new InRangeConstraint();
2201                                             Object minValue = rangeArray.get(0);
2202                                             Object maxValue = rangeArray.get(1);
2203                                             rangeConstraint.setRangeMinValue(minValue);
2204                                             rangeConstraint.setRangeMaxValue(maxValue);
2205                                             propertyConstraint = rangeConstraint;
2206                                         }
2207                                     }
2208                                 } else {
2209                                     log.warn(THE_VALUE_OF_GREATER_THAN_CONSTRAINT_IS_NULL);
2210                                 }
2211                                 break;
2212                             case GREATER_THAN:
2213                                 if (typedValue != null) {
2214                                     log.debug("Before adding value to GreaterThanConstraint object. value = {}", typedValue);
2215                                     propertyConstraint = new GreaterThanConstraint(typedValue);
2216                                     break;
2217                                 } else {
2218                                     log.warn(THE_VALUE_OF_GREATER_THAN_CONSTRAINT_IS_NULL);
2219                                 }
2220                                 break;
2221                             case LESS_THAN:
2222                                 if (typedValue != null) {
2223                                     log.debug("Before adding value to LessThanConstraint object. value = {}", typedValue);
2224                                     propertyConstraint = new LessThanConstraint(typedValue);
2225                                     break;
2226                                 } else {
2227                                     log.warn("The value of LessThanConstraint is null");
2228                                 }
2229                                 break;
2230                             case GREATER_OR_EQUAL:
2231                                 if (typedValue != null) {
2232                                     log.debug("Before adding value to GreaterThanConstraint object. value = {}", typedValue);
2233                                     propertyConstraint = new GreaterOrEqualConstraint(typedValue);
2234                                     break;
2235                                 } else {
2236                                     log.warn("The value of GreaterOrEqualConstraint is null");
2237                                 }
2238                                 break;
2239                             case LESS_OR_EQUAL:
2240                                 if (typedValue != null) {
2241                                     log.debug("Before adding value to LessOrEqualConstraint object. value = {}", typedValue);
2242                                     propertyConstraint = new LessOrEqualConstraint(typedValue);
2243                                 } else {
2244                                     log.warn(THE_VALUE_OF_GREATER_THAN_CONSTRAINT_IS_NULL);
2245                                 }
2246                                 break;
2247                             case VALID_VALUES:
2248                                 if (typedValue != null) {
2249                                     ArrayList validValuesArray = (ArrayList) typedValue;
2250                                     if (validValuesArray.size() == 0 || validValuesArray.contains("")) {
2251                                         log.error("The valid values constraint content is invalid. value = {}", typedValue);
2252                                         throw new JsonSyntaxException("The valid values constraint content is invalid");
2253                                     } else {
2254                                         ValidValuesConstraint vvConstraint = new ValidValuesConstraint();
2255                                         vvConstraint.setValidValues(validValuesArray);
2256                                         propertyConstraint = vvConstraint;
2257                                     }
2258                                 }
2259                                 break;
2260                             case LENGTH:
2261                                 if (value != null) {
2262                                     int asInt = value.getAsInt();
2263                                     log.debug("Before adding value to length constraint. value = {}", asInt);
2264                                     propertyConstraint = new LengthConstraint(asInt);
2265                                     break;
2266                                 } else {
2267                                     log.warn("The value of length constraint is null");
2268                                 }
2269                                 break;
2270                             case MIN_LENGTH:
2271                                 if (value != null) {
2272                                     int asInt = value.getAsInt();
2273                                     log.debug("Before adding value to Min Length object. value = {}", asInt);
2274                                     propertyConstraint = new MinLengthConstraint(asInt);
2275                                     break;
2276                                 } else {
2277                                     log.warn("The value of MinLengthConstraint is null");
2278                                 }
2279                                 break;
2280                             case MAX_LENGTH:
2281                                 if (value != null) {
2282                                     int asInt = value.getAsInt();
2283                                     log.debug("Before adding value to max length constraint. value = {}", asInt);
2284                                     propertyConstraint = new MaxLengthConstraint(asInt);
2285                                     break;
2286                                 } else {
2287                                     log.warn("The value of max length constraint is null");
2288                                 }
2289                                 break;
2290                             case PATTERN:
2291                                 if (value != null) {
2292                                     String asString = value.getAsString();
2293                                     log.debug("Before adding value to PatternConstraint object. value = {}", asString);
2294                                     propertyConstraint = new PatternConstraint(asString);
2295                                     break;
2296                                 } else {
2297                                     log.warn("The value of pattern constraint is null");
2298                                 }
2299                                 break;
2300                             default:
2301                                 log.warn("Key {} is not supported. Ignored.", key);
2302                         }
2303                     }
2304                 }
2305             }
2306             return propertyConstraint;
2307         }
2308
2309         private Object getTypedValue(JsonElement je) {
2310             if (je == null || je.isJsonNull()) {
2311                 return null;
2312             }
2313             if (je.isJsonPrimitive()) {
2314                 return getJsonPrimitive(je.getAsJsonPrimitive());
2315             }
2316             if (je.isJsonArray()) {
2317                 ArrayList<Object> array = new ArrayList<>();
2318                 for (JsonElement e : je.getAsJsonArray()) {
2319                     array.add(getJsonPrimitive(e.getAsJsonPrimitive()));
2320                 }
2321                 return array;
2322             }
2323             return je;
2324         }
2325
2326         private Object getJsonPrimitive(JsonPrimitive je) {
2327             if (je.isBoolean()) {
2328                 return je.getAsBoolean();
2329             }
2330             if (je.isString()) {
2331                 return je.getAsString();
2332             }
2333             if (je.isNumber()) {
2334                 double number = je.getAsNumber().floatValue();
2335                 if ((number % 1) == 0) {
2336                     return je.getAsNumber().intValue();
2337                 }
2338                 return number;
2339             }
2340             return null;
2341         }
2342     }
2343
2344     public static class PropertyConstraintJacksonDeserializer extends com.fasterxml.jackson.databind.JsonDeserializer<PropertyConstraint> {
2345
2346         @Override
2347         public PropertyConstraint deserialize(com.fasterxml.jackson.core.JsonParser json, DeserializationContext context) throws IOException {
2348             ObjectCodec oc = json.getCodec();
2349             JsonNode node = oc.readTree(json);
2350             PropertyConstraint propertyConstraint = null;
2351
2352             Iterator<Entry<String, JsonNode>> fieldsIterator = node.fields();
2353             while (fieldsIterator.hasNext()) {
2354                 Entry<String, JsonNode> field = fieldsIterator.next();
2355                 ConstraintType constraintType = ConstraintType.findByType(field.getKey()).orElse(null);
2356                 JsonNode value = field.getValue();
2357
2358                 if (constraintType == null) {
2359                     log.warn("ConstraintType was not found for constraint name:{}", field.getKey());
2360                 } else {
2361                     if (value == null) {
2362                         log.warn("The value of {} constraint is null", constraintType);
2363                     } else {
2364                         switch (constraintType) {
2365                             case EQUAL:
2366                                 propertyConstraint = deserializeConstraintWithStringOperand(value, EqualConstraint.class);
2367                                 break;
2368                             case IN_RANGE:
2369                                 propertyConstraint = deserializeInRangeConstraintConstraint(value);
2370                                 break;
2371                             case GREATER_THAN:
2372                                 propertyConstraint = deserializeConstraintWithStringOperand(value, GreaterThanConstraint.class);
2373                                 break;
2374                             case LESS_THAN:
2375                                 propertyConstraint = deserializeConstraintWithStringOperand(value, LessThanConstraint.class);
2376                                 break;
2377                             case GREATER_OR_EQUAL:
2378                                 propertyConstraint = deserializeConstraintWithStringOperand(value, GreaterOrEqualConstraint.class);
2379                                 break;
2380                             case LESS_OR_EQUAL:
2381                                 propertyConstraint = deserializeConstraintWithStringOperand(value, LessOrEqualConstraint.class);
2382                                 break;
2383                             case VALID_VALUES:
2384                                 propertyConstraint = deserializeValidValuesConstraint(value);
2385                                 break;
2386                             case LENGTH:
2387                                 propertyConstraint = deserializeConstraintWithIntegerOperand(value, LengthConstraint.class);
2388                                 break;
2389                             case MIN_LENGTH:
2390                                 propertyConstraint = deserializeConstraintWithIntegerOperand(value, MinLengthConstraint.class);
2391                                 break;
2392                             case MAX_LENGTH:
2393                                 propertyConstraint = deserializeConstraintWithIntegerOperand(value, MaxLengthConstraint.class);
2394                                 break;
2395                             case PATTERN:
2396                                 propertyConstraint = deserializeConstraintWithStringPatternOperand(value, PatternConstraint.class);
2397                                 break;
2398                             default:
2399                                 log.warn("Key {} is not supported. Ignored.", field.getKey());
2400                         }
2401                     }
2402                 }
2403             }
2404
2405             return propertyConstraint;
2406         }
2407
2408         private PropertyConstraint deserializeConstraintWithStringOperand(JsonNode value, Class<? extends PropertyConstraint> constraintClass) {
2409             String asString = value.asText();
2410             log.debug("Before adding value to {} object. value = {}", constraintClass, asString);
2411             try {
2412                 return constraintClass.getConstructor(Object.class).newInstance(asString);
2413             } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException
2414                      | SecurityException exception) {
2415                 log.error("Error deserializing constraint", exception);
2416                 return null;
2417             }
2418         }
2419
2420         private PropertyConstraint deserializeConstraintWithStringPatternOperand(JsonNode value,
2421                                                                                  Class<? extends PropertyConstraint> constraintClass) {
2422             String asString = value.asText();
2423             log.debug("Before adding value to {} object. value = {}", constraintClass, asString);
2424             try {
2425                 return constraintClass.getConstructor(String.class).newInstance(asString);
2426             } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException
2427                      | SecurityException exception) {
2428                 log.error("Error deserializing constraint", exception);
2429                 return null;
2430             }
2431         }
2432
2433         private PropertyConstraint deserializeConstraintWithIntegerOperand(JsonNode value, Class<? extends PropertyConstraint> constraintClass) {
2434             Integer asInt = value.asInt();
2435             log.debug("Before adding value to {} object. value = {}", constraintClass, asInt);
2436             try {
2437                 return constraintClass.getConstructor(Integer.class).newInstance(asInt);
2438             } catch (InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException | NoSuchMethodException
2439                      | SecurityException exception) {
2440                 log.error("Error deserializing constraint", exception);
2441                 return null;
2442             }
2443         }
2444
2445         private PropertyConstraint deserializeInRangeConstraintConstraint(JsonNode value) {
2446             if (value instanceof ArrayNode) {
2447                 ArrayNode rangeArray = (ArrayNode) value;
2448                 if (rangeArray.size() != 2) {
2449                     log.error("The range constraint content is invalid. value = {}", value);
2450                 } else {
2451                     InRangeConstraint rangeConstraint = new InRangeConstraint();
2452                     String minValue = rangeArray.get(0).asText();
2453                     String maxValue;
2454                     JsonNode maxElement = rangeArray.get(1);
2455                     if (maxElement.isNull()) {
2456                         maxValue = null;
2457                     } else {
2458                         maxValue = maxElement.asText();
2459                     }
2460                     rangeConstraint.setRangeMinValue(minValue);
2461                     rangeConstraint.setRangeMaxValue(maxValue);
2462                     return rangeConstraint;
2463                 }
2464             }
2465             return null;
2466         }
2467
2468         private PropertyConstraint deserializeValidValuesConstraint(JsonNode value) {
2469             ArrayNode rangeArray = (ArrayNode) value;
2470             if (rangeArray.size() == 0) {
2471                 log.error("The valid values constraint content is invalid. value = {}", value);
2472             } else {
2473                 ValidValuesConstraint vvConstraint = new ValidValuesConstraint();
2474                 List<Object> validValues = new ArrayList<>();
2475                 for (JsonNode jsonElement : rangeArray) {
2476                     String item = jsonElement.asText();
2477                     validValues.add(item);
2478                 }
2479                 vvConstraint.setValidValues(validValues);
2480                 return vvConstraint;
2481             }
2482             return null;
2483         }
2484
2485     }
2486
2487 }