Reformat catalog-model
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / operations / impl / AttributeOperation.java
1 /*
2  * ============LICENSE_START=======================================================
3  *  SDC
4  *  Copyright (C) 2020 Nordix Foundation
5  *  ================================================================================
6  *  Licensed under the Apache License, Version 2.0 (the "License");
7  *  you may not use this file except in compliance with the License.
8  *  You may obtain a copy of the License at
9  *
10  *        http://www.apache.org/licenses/LICENSE-2.0
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *  SPDX-License-Identifier: Apache-2.0
18  *  ============LICENSE_END=========================================================
19  */
20 package org.openecomp.sdc.be.model.operations.impl;
21
22 import com.google.gson.JsonElement;
23 import fj.data.Either;
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Map.Entry;
29 import org.apache.commons.lang3.tuple.ImmutablePair;
30 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
31 import org.openecomp.sdc.be.dao.janusgraph.HealingJanusGraphGenericDao;
32 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
33 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
34 import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
35 import org.openecomp.sdc.be.datatypes.elements.AttributeDataDefinition;
36 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
37 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
38 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
39 import org.openecomp.sdc.be.model.DataTypeDefinition;
40 import org.openecomp.sdc.be.model.PropertyDefinition;
41 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
42 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
43 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
44 import org.openecomp.sdc.be.resources.data.DataTypeData;
45 import org.openecomp.sdc.be.resources.data.PropertyData;
46 import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
47 import org.openecomp.sdc.common.log.wrappers.Logger;
48 import org.springframework.beans.factory.annotation.Autowired;
49 import org.springframework.stereotype.Component;
50
51 @Component("attribute-operation")
52 public class AttributeOperation extends AbstractOperation {
53
54     private static final String FAILED_TO_FETCH_ATTRIBUTES_OF_DATA_TYPE = "Failed to fetch attributes of data type {}";
55     private static final String DATA_TYPE_CANNOT_BE_FOUND_IN_GRAPH_STATUS_IS = "Data type {} cannot be found in graph. status is {}";
56     private static final String THE_VALUE_OF_ATTRIBUTE_FROM_TYPE_IS_INVALID = "The value {} of attribute from type {} is invalid";
57     private static Logger log = Logger.getLogger(AttributeOperation.class.getName());
58
59     @Autowired
60     public AttributeOperation(HealingJanusGraphGenericDao janusGraphGenericDao) {
61         this.janusGraphGenericDao = janusGraphGenericDao;
62     }
63
64     public boolean isAttributeTypeValid(final AttributeDataDefinition attributeDefinition) {
65         if (attributeDefinition == null) {
66             return false;
67         }
68         if (ToscaPropertyType.isValidType(attributeDefinition.getType()) == null) {
69             final Either<Boolean, JanusGraphOperationStatus> definedInDataTypes = isDefinedInDataTypes(attributeDefinition.getType());
70             if (definedInDataTypes.isRight()) {
71                 return false;
72             } else {
73                 Boolean isExist = definedInDataTypes.left().value();
74                 return isExist.booleanValue();
75             }
76         }
77         return true;
78     }
79
80     public Either<Boolean, JanusGraphOperationStatus> isDefinedInDataTypes(final String propertyType) {
81         final String dataTypeUid = UniqueIdBuilder.buildDataTypeUid(propertyType);
82         final Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeByUid = getDataTypeByUid(dataTypeUid);
83         if (dataTypeByUid.isRight()) {
84             final JanusGraphOperationStatus status = dataTypeByUid.right().value();
85             if (status == JanusGraphOperationStatus.NOT_FOUND) {
86                 return Either.left(false);
87             }
88             return Either.right(status);
89         }
90         return Either.left(true);
91     }
92
93     /**
94      * Build Data type object from graph by unique id
95      */
96     public Either<DataTypeDefinition, JanusGraphOperationStatus> getDataTypeByUid(final String uniqueId) {
97         final Either<DataTypeData, JanusGraphOperationStatus> dataTypesRes = janusGraphGenericDao
98             .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, DataTypeData.class);
99         if (dataTypesRes.isRight()) {
100             JanusGraphOperationStatus status = dataTypesRes.right().value();
101             log.debug(DATA_TYPE_CANNOT_BE_FOUND_IN_GRAPH_STATUS_IS, uniqueId, status);
102             return Either.right(status);
103         }
104         final DataTypeData ctData = dataTypesRes.left().value();
105         final DataTypeDefinition dataTypeDefinition = new DataTypeDefinition(ctData.getDataTypeDataDefinition());
106         final JanusGraphOperationStatus propertiesStatus = fillProperties(uniqueId, dataTypeDefinition);
107         if (propertiesStatus != JanusGraphOperationStatus.OK) {
108             log.error(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR, FAILED_TO_FETCH_ATTRIBUTES_OF_DATA_TYPE, uniqueId);
109             return Either.right(propertiesStatus);
110         }
111         final Either<ImmutablePair<DataTypeData, GraphEdge>, JanusGraphOperationStatus> parentNode = janusGraphGenericDao
112             .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.DataType,
113                 DataTypeData.class);
114         log.debug("After retrieving DERIVED_FROM node of {}. status is {}", uniqueId, parentNode);
115         if (parentNode.isRight()) {
116             final JanusGraphOperationStatus janusGraphOperationStatus = parentNode.right().value();
117             if (janusGraphOperationStatus != JanusGraphOperationStatus.NOT_FOUND) {
118                 log.error(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR, "Failed to find the parent data type of data type {}. status is {}", uniqueId,
119                     janusGraphOperationStatus);
120                 return Either.right(janusGraphOperationStatus);
121             }
122         } else {
123             // derived from node was found
124             final ImmutablePair<DataTypeData, GraphEdge> immutablePair = parentNode.left().value();
125             final DataTypeData parentCT = immutablePair.getKey();
126             final String parentUniqueId = parentCT.getUniqueId();
127             final Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeByUid = getDataTypeByUid(parentUniqueId);
128             if (dataTypeByUid.isRight()) {
129                 return Either.right(dataTypeByUid.right().value());
130             }
131             final DataTypeDefinition parentDataTypeDefinition = dataTypeByUid.left().value();
132             dataTypeDefinition.setDerivedFrom(parentDataTypeDefinition);
133         }
134         return Either.left(dataTypeDefinition);
135     }
136
137     private JanusGraphOperationStatus fillProperties(final String uniqueId, final DataTypeDefinition dataTypeDefinition) {
138         final Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesOfNode = this
139             .findPropertiesOfNode(NodeTypeEnum.DataType, uniqueId);
140         if (findPropertiesOfNode.isRight()) {
141             final JanusGraphOperationStatus janusGraphOperationStatus = findPropertiesOfNode.right().value();
142             log.debug("After looking for properties of vertex {}. status is {}", uniqueId, janusGraphOperationStatus);
143             if (JanusGraphOperationStatus.NOT_FOUND.equals(janusGraphOperationStatus)) {
144                 return JanusGraphOperationStatus.OK;
145             } else {
146                 return janusGraphOperationStatus;
147             }
148         } else {
149             final Map<String, PropertyDefinition> properties = findPropertiesOfNode.left().value();
150             if (properties != null && !properties.isEmpty()) {
151                 List<PropertyDefinition> listOfProps = new ArrayList<>();
152                 for (final Entry<String, PropertyDefinition> entry : properties.entrySet()) {
153                     final String propName = entry.getKey();
154                     final PropertyDefinition propertyDefinition = entry.getValue();
155                     final PropertyDefinition newPropertyDefinition = new PropertyDefinition(propertyDefinition);
156                     newPropertyDefinition.setName(propName);
157                     listOfProps.add(newPropertyDefinition);
158                 }
159                 dataTypeDefinition.setProperties(listOfProps);
160             }
161             return JanusGraphOperationStatus.OK;
162         }
163     }
164
165     public Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesOfNode(final NodeTypeEnum nodeType,
166                                                                                                    final String uniqueId) {
167         final Map<String, PropertyDefinition> resourceProps = new HashMap<>();
168         final Either<List<ImmutablePair<PropertyData, GraphEdge>>, JanusGraphOperationStatus> childrenNodes = janusGraphGenericDao
169             .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), uniqueId, GraphEdgeLabels.PROPERTY, NodeTypeEnum.Property,
170                 PropertyData.class);
171         if (childrenNodes.isRight()) {
172             final JanusGraphOperationStatus operationStatus = childrenNodes.right().value();
173             return Either.right(operationStatus);
174         }
175         final List<ImmutablePair<PropertyData, GraphEdge>> values = childrenNodes.left().value();
176         if (values != null) {
177             for (final ImmutablePair<PropertyData, GraphEdge> immutablePair : values) {
178                 final GraphEdge edge = immutablePair.getValue();
179                 final String propertyName = (String) edge.getProperties().get(GraphPropertiesDictionary.NAME.getProperty());
180                 log.debug("Attribute {} is associated to node {}", propertyName, uniqueId);
181                 final PropertyData propertyData = immutablePair.getKey();
182                 final PropertyDefinition propertyDefinition = this.convertPropertyDataToPropertyDefinition(propertyData, propertyName);
183                 resourceProps.put(propertyName, propertyDefinition);
184             }
185         }
186         log.debug("The properties associated to node {} are {}", uniqueId, resourceProps);
187         return Either.left(resourceProps);
188     }
189
190     public PropertyDefinition convertPropertyDataToPropertyDefinition(final PropertyData propertyDataResult, final String propertyName) {
191         log.debug("The object returned after create property is {}", propertyDataResult);
192         final PropertyDefinition propertyDefResult = new PropertyDefinition(propertyDataResult.getPropertyDataDefinition());
193         propertyDefResult.setConstraints(convertConstraints(propertyDataResult.getConstraints()));
194         propertyDefResult.setName(propertyName);
195         return propertyDefResult;
196     }
197
198     public ImmutablePair<String, Boolean> isAttributeInnerTypeValid(final AttributeDataDefinition attributeDefinition,
199                                                                     final Map<String, DataTypeDefinition> dataTypes) {
200         if (attributeDefinition == null) {
201             return new ImmutablePair<>(null, false);
202         }
203         SchemaDefinition schema;
204         PropertyDataDefinition innerProp;
205         String innerType = null;
206         if ((schema = attributeDefinition.getSchema()) != null && ((innerProp = schema.getProperty()) != null)) {
207             innerType = innerProp.getType();
208         }
209         final ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(innerType);
210         if (innerToscaType == null) {
211             final DataTypeDefinition dataTypeDefinition = dataTypes.get(innerType);
212             if (dataTypeDefinition == null) {
213                 log.debug("The inner type {} is not a data type.", innerType);
214                 return new ImmutablePair<>(innerType, false);
215             } else {
216                 log.debug("The inner type {} is a data type. Data type definition is {}", innerType, dataTypeDefinition);
217             }
218         }
219         return new ImmutablePair<>(innerType, true);
220     }
221
222     public boolean isAttributeDefaultValueValid(final AttributeDataDefinition attributeDefinition, final Map<String, DataTypeDefinition> dataTypes) {
223         if (attributeDefinition == null) {
224             return false;
225         }
226         boolean isValid;
227         String innerType = null;
228         final String propertyType = attributeDefinition.getType();
229         final ToscaPropertyType type = getType(propertyType);
230         if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
231             final SchemaDefinition def = attributeDefinition.getSchema();
232             if (def == null) {
233                 return false;
234             }
235             final PropertyDataDefinition propDef = def.getProperty();
236             if (propDef == null) {
237                 return false;
238             }
239             innerType = propDef.getType();
240         }
241         final String value = (String) attributeDefinition.get_default();
242         if (type != null) {
243             isValid = isValidValue(type, value, innerType, dataTypes);
244         } else {
245             log.trace("The given type {} is not a pre defined one.", propertyType);
246             final DataTypeDefinition foundDt = dataTypes.get(propertyType);
247             if (foundDt != null) {
248                 isValid = isValidComplexValue(foundDt, value, dataTypes);
249             } else {
250                 isValid = false;
251             }
252         }
253         return isValid;
254     }
255
256     private boolean isValidComplexValue(final DataTypeDefinition foundDt, final String value, final Map<String, DataTypeDefinition> dataTypes) {
257         final ImmutablePair<JsonElement, Boolean> validateAndUpdate = dataTypeValidatorConverter.validateAndUpdate(value, foundDt, dataTypes);
258         log.trace("The result after validating complex value of type {} is {}", foundDt.getName(), validateAndUpdate);
259         return validateAndUpdate.right.booleanValue();
260     }
261
262     public StorageOperationStatus validateAndUpdateAttribute(final AttributeDataDefinition attributeDefinition,
263                                                              final Map<String, DataTypeDefinition> dataTypes) {
264         log.trace("Going to validate attribute type and value. {}", attributeDefinition);
265         final String attributeDefinitionType = attributeDefinition.getType();
266         final String value = (String) attributeDefinition.get_default();
267         final ToscaPropertyType type = getType(attributeDefinitionType);
268         if (type == null) {
269             final DataTypeDefinition dataTypeDefinition = dataTypes.get(attributeDefinitionType);
270             if (dataTypeDefinition == null) {
271                 log.debug("The type {} of attribute cannot be found.", attributeDefinitionType);
272                 return StorageOperationStatus.INVALID_TYPE;
273             }
274             return validateAndUpdateAttributeComplexValue(attributeDefinition, attributeDefinitionType, value, dataTypeDefinition, dataTypes);
275         }
276         String innerType;
277         final Either<String, JanusGraphOperationStatus> checkInnerType = getInnerType(type, attributeDefinition::getSchema);
278         if (checkInnerType.isRight()) {
279             return StorageOperationStatus.INVALID_TYPE;
280         }
281         innerType = checkInnerType.left().value();
282         log.trace("After validating property type {}", attributeDefinitionType);
283         if (!isValidValue(type, value, innerType, dataTypes)) {
284             log.info(THE_VALUE_OF_ATTRIBUTE_FROM_TYPE_IS_INVALID, value, type);
285             return StorageOperationStatus.INVALID_VALUE;
286         }
287         final PropertyValueConverter converter = type.getConverter();
288         if (isEmptyValue(value)) {
289             log.debug("Default value was not sent for attribute {}. Set default value to {}", attributeDefinition.getName(), EMPTY_VALUE);
290             attributeDefinition.set_default(EMPTY_VALUE);
291         } else if (!isEmptyValue(value)) {
292             attributeDefinition.set_default(converter.convert(value, innerType, dataTypes));
293         }
294         return StorageOperationStatus.OK;
295     }
296
297     private StorageOperationStatus validateAndUpdateAttributeComplexValue(final AttributeDataDefinition attributeDefinition,
298                                                                           final String attributeType, final String value,
299                                                                           final DataTypeDefinition dataTypeDefinition,
300                                                                           final Map<String, DataTypeDefinition> dataTypes) {
301         final ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
302         if (!validateResult.right.booleanValue()) {
303             log.debug(THE_VALUE_OF_ATTRIBUTE_FROM_TYPE_IS_INVALID, attributeType, attributeType);
304             return StorageOperationStatus.INVALID_VALUE;
305         }
306         final JsonElement jsonElement = validateResult.left;
307         if (log.isTraceEnabled()) {
308             log.trace("Going to update value in attribute definition {} {}", attributeDefinition.getName(),
309                 (jsonElement != null ? jsonElement.toString() : null));
310         }
311         updateAttributeValue(attributeDefinition, jsonElement);
312         return StorageOperationStatus.OK;
313     }
314
315     private void updateAttributeValue(final AttributeDataDefinition attributeDefinition, final JsonElement jsonElement) {
316         attributeDefinition.set_default(jsonElement);
317     }
318 }