Fix import VFC with attributes
[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
21 package org.openecomp.sdc.be.model.operations.impl;
22
23 import com.google.gson.JsonElement;
24 import fj.data.Either;
25 import java.util.ArrayList;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Map.Entry;
30 import org.apache.commons.lang3.tuple.ImmutablePair;
31 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
32 import org.openecomp.sdc.be.dao.janusgraph.HealingJanusGraphGenericDao;
33 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
34 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
35 import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
36 import org.openecomp.sdc.be.datatypes.elements.AttributeDataDefinition;
37 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
38 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
39 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
40 import org.openecomp.sdc.be.model.DataTypeDefinition;
41 import org.openecomp.sdc.be.model.PropertyDefinition;
42 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
43 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
44 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
45 import org.openecomp.sdc.be.resources.data.DataTypeData;
46 import org.openecomp.sdc.be.resources.data.PropertyData;
47 import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
48 import org.openecomp.sdc.common.log.wrappers.Logger;
49 import org.springframework.beans.factory.annotation.Autowired;
50 import org.springframework.stereotype.Component;
51
52
53 @Component("attribute-operation")
54 public class AttributeOperation extends AbstractOperation {
55
56     private static Logger log = Logger.getLogger(AttributeOperation.class.getName());
57
58     private static final String FAILED_TO_FETCH_ATTRIBUTES_OF_DATA_TYPE = "Failed to fetch attributes of data type {}";
59     private static final String DATA_TYPE_CANNOT_BE_FOUND_IN_GRAPH_STATUS_IS = "Data type {} cannot be found in graph. status is {}";
60     private static final String THE_VALUE_OF_ATTRIBUTE_FROM_TYPE_IS_INVALID = "The value {} of attribute from type {} is invalid";
61
62
63     @Autowired
64     public AttributeOperation(HealingJanusGraphGenericDao janusGraphGenericDao) {
65         this.janusGraphGenericDao = janusGraphGenericDao;
66     }
67
68     public boolean isAttributeTypeValid(final AttributeDataDefinition attributeDefinition) {
69
70         if (attributeDefinition == null) {
71             return false;
72         }
73
74         if (ToscaPropertyType.isValidType(attributeDefinition.getType()) == null) {
75             final Either<Boolean, JanusGraphOperationStatus> definedInDataTypes = isDefinedInDataTypes(
76                 attributeDefinition.getType());
77
78             if (definedInDataTypes.isRight()) {
79                 return false;
80             } else {
81                 Boolean isExist = definedInDataTypes.left().value();
82                 return isExist.booleanValue();
83             }
84
85         }
86         return true;
87     }
88
89     public Either<Boolean, JanusGraphOperationStatus> isDefinedInDataTypes(final String propertyType) {
90
91         final String dataTypeUid = UniqueIdBuilder.buildDataTypeUid(propertyType);
92         final Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeByUid = getDataTypeByUid(dataTypeUid);
93         if (dataTypeByUid.isRight()) {
94             final JanusGraphOperationStatus status = dataTypeByUid.right().value();
95             if (status == JanusGraphOperationStatus.NOT_FOUND) {
96                 return Either.left(false);
97             }
98             return Either.right(status);
99         }
100
101         return Either.left(true);
102
103     }
104
105     /**
106      * Build Data type object from graph by unique id
107      */
108     public Either<DataTypeDefinition, JanusGraphOperationStatus> getDataTypeByUid(final String uniqueId) {
109
110         final Either<DataTypeData, JanusGraphOperationStatus> dataTypesRes = janusGraphGenericDao
111             .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, DataTypeData.class);
112
113         if (dataTypesRes.isRight()) {
114             JanusGraphOperationStatus status = dataTypesRes.right().value();
115             log.debug(DATA_TYPE_CANNOT_BE_FOUND_IN_GRAPH_STATUS_IS, uniqueId, status);
116             return Either.right(status);
117         }
118
119         final DataTypeData ctData = dataTypesRes.left().value();
120         final DataTypeDefinition dataTypeDefinition = new DataTypeDefinition(ctData.getDataTypeDataDefinition());
121
122         final JanusGraphOperationStatus propertiesStatus = fillProperties(uniqueId, dataTypeDefinition);
123         if (propertiesStatus != JanusGraphOperationStatus.OK) {
124             log.error(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR, FAILED_TO_FETCH_ATTRIBUTES_OF_DATA_TYPE, uniqueId);
125             return Either.right(propertiesStatus);
126         }
127
128         final Either<ImmutablePair<DataTypeData, GraphEdge>, JanusGraphOperationStatus> parentNode = janusGraphGenericDao
129             .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, GraphEdgeLabels.DERIVED_FROM,
130                 NodeTypeEnum.DataType,
131                 DataTypeData.class);
132         log.debug("After retrieving DERIVED_FROM node of {}. status is {}", uniqueId, parentNode);
133         if (parentNode.isRight()) {
134             final JanusGraphOperationStatus janusGraphOperationStatus = parentNode.right().value();
135             if (janusGraphOperationStatus != JanusGraphOperationStatus.NOT_FOUND) {
136                 log.error(EcompLoggerErrorCode.BUSINESS_PROCESS_ERROR,
137                     "Failed to find the parent data type of data type {}. status is {}", uniqueId,
138                     janusGraphOperationStatus);
139                 return Either.right(janusGraphOperationStatus);
140             }
141         } else {
142             // derived from node was found
143             final ImmutablePair<DataTypeData, GraphEdge> immutablePair = parentNode.left().value();
144             final DataTypeData parentCT = immutablePair.getKey();
145
146             final String parentUniqueId = parentCT.getUniqueId();
147             final Either<DataTypeDefinition, JanusGraphOperationStatus> dataTypeByUid = getDataTypeByUid(
148                 parentUniqueId);
149
150             if (dataTypeByUid.isRight()) {
151                 return Either.right(dataTypeByUid.right().value());
152             }
153
154             final DataTypeDefinition parentDataTypeDefinition = dataTypeByUid.left().value();
155
156             dataTypeDefinition.setDerivedFrom(parentDataTypeDefinition);
157         }
158
159         return Either.left(dataTypeDefinition);
160     }
161
162     private JanusGraphOperationStatus fillProperties(final String uniqueId,
163                                                      final DataTypeDefinition dataTypeDefinition) {
164
165         final Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesOfNode = this
166             .findPropertiesOfNode(NodeTypeEnum.DataType, uniqueId);
167         if (findPropertiesOfNode.isRight()) {
168             final JanusGraphOperationStatus janusGraphOperationStatus = findPropertiesOfNode.right().value();
169             log.debug("After looking for properties of vertex {}. status is {}", uniqueId,
170                 janusGraphOperationStatus);
171             if (JanusGraphOperationStatus.NOT_FOUND.equals(janusGraphOperationStatus)) {
172                 return JanusGraphOperationStatus.OK;
173             } else {
174                 return janusGraphOperationStatus;
175             }
176         } else {
177             final Map<String, PropertyDefinition> properties = findPropertiesOfNode.left().value();
178             if (properties != null && !properties.isEmpty()) {
179                 List<PropertyDefinition> listOfProps = new ArrayList<>();
180
181                 for (final Entry<String, PropertyDefinition> entry : properties.entrySet()) {
182                     final String propName = entry.getKey();
183                     final PropertyDefinition propertyDefinition = entry.getValue();
184                     final PropertyDefinition newPropertyDefinition = new PropertyDefinition(propertyDefinition);
185                     newPropertyDefinition.setName(propName);
186                     listOfProps.add(newPropertyDefinition);
187                 }
188                 dataTypeDefinition.setProperties(listOfProps);
189             }
190             return JanusGraphOperationStatus.OK;
191         }
192     }
193
194     public Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> findPropertiesOfNode(
195         final NodeTypeEnum nodeType, final String uniqueId) {
196
197         final Map<String, PropertyDefinition> resourceProps = new HashMap<>();
198
199         final Either<List<ImmutablePair<PropertyData, GraphEdge>>, JanusGraphOperationStatus> childrenNodes = janusGraphGenericDao
200             .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(nodeType), uniqueId, GraphEdgeLabels.PROPERTY,
201                 NodeTypeEnum.Property,
202                 PropertyData.class);
203
204         if (childrenNodes.isRight()) {
205             final JanusGraphOperationStatus operationStatus = childrenNodes.right().value();
206             return Either.right(operationStatus);
207         }
208
209         final List<ImmutablePair<PropertyData, GraphEdge>> values = childrenNodes.left().value();
210         if (values != null) {
211             for (final ImmutablePair<PropertyData, GraphEdge> immutablePair : values) {
212                 final GraphEdge edge = immutablePair.getValue();
213                 final String propertyName = (String) edge.getProperties().get(GraphPropertiesDictionary.NAME.getProperty());
214                 log.debug("Attribute {} is associated to node {}", propertyName, uniqueId);
215                 final PropertyData propertyData = immutablePair.getKey();
216                 final PropertyDefinition propertyDefinition = this
217                     .convertPropertyDataToPropertyDefinition(propertyData, propertyName);
218                 resourceProps.put(propertyName, propertyDefinition);
219             }
220
221         }
222
223         log.debug("The properties associated to node {} are {}", uniqueId, resourceProps);
224         return Either.left(resourceProps);
225     }
226
227     public PropertyDefinition convertPropertyDataToPropertyDefinition(final PropertyData propertyDataResult,
228                                                                       final String propertyName) {
229         log.debug("The object returned after create property is {}", propertyDataResult);
230
231         final PropertyDefinition propertyDefResult = new PropertyDefinition(propertyDataResult.getPropertyDataDefinition());
232         propertyDefResult.setConstraints(convertConstraints(propertyDataResult.getConstraints()));
233         propertyDefResult.setName(propertyName);
234
235         return propertyDefResult;
236     }
237
238
239     public ImmutablePair<String, Boolean> isAttributeInnerTypeValid(final AttributeDataDefinition attributeDefinition,
240                                                                     final Map<String, DataTypeDefinition> dataTypes) {
241
242         if (attributeDefinition == null) {
243             return new ImmutablePair<>(null, false);
244         }
245
246         SchemaDefinition schema;
247         PropertyDataDefinition innerProp;
248         String innerType = null;
249         if ((schema = attributeDefinition.getSchema()) != null && ((innerProp = schema.getProperty()) != null)) {
250             innerType = innerProp.getType();
251         }
252
253         final ToscaPropertyType innerToscaType = ToscaPropertyType.isValidType(innerType);
254
255         if (innerToscaType == null) {
256             final DataTypeDefinition dataTypeDefinition = dataTypes.get(innerType);
257             if (dataTypeDefinition == null) {
258                 log.debug("The inner type {} is not a data type.", innerType);
259                 return new ImmutablePair<>(innerType, false);
260             } else {
261                 log.debug("The inner type {} is a data type. Data type definition is {}", innerType,
262                     dataTypeDefinition);
263             }
264         }
265         return new ImmutablePair<>(innerType, true);
266     }
267
268
269     public boolean isAttributeDefaultValueValid(final AttributeDataDefinition attributeDefinition,
270                                                 final Map<String, DataTypeDefinition> dataTypes) {
271         if (attributeDefinition == null) {
272             return false;
273         }
274         boolean isValid;
275         String innerType = null;
276         final String propertyType = attributeDefinition.getType();
277         final ToscaPropertyType type = getType(propertyType);
278         if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
279             final SchemaDefinition def = attributeDefinition.getSchema();
280             if (def == null) {
281                 return false;
282             }
283             final PropertyDataDefinition propDef = def.getProperty();
284             if (propDef == null) {
285                 return false;
286             }
287             innerType = propDef.getType();
288         }
289         final String value = (String) attributeDefinition.get_default();
290         if (type != null) {
291             isValid = isValidValue(type, value, innerType, dataTypes);
292         } else {
293             log.trace("The given type {} is not a pre defined one.", propertyType);
294
295             final DataTypeDefinition foundDt = dataTypes.get(propertyType);
296             if (foundDt != null) {
297                 isValid = isValidComplexValue(foundDt, value, dataTypes);
298             } else {
299                 isValid = false;
300             }
301         }
302         return isValid;
303     }
304
305     private boolean isValidComplexValue(final DataTypeDefinition foundDt, final String value,
306                                         final Map<String, DataTypeDefinition> dataTypes) {
307         final ImmutablePair<JsonElement, Boolean> validateAndUpdate = dataTypeValidatorConverter
308             .validateAndUpdate(value, foundDt, dataTypes);
309
310         log.trace("The result after validating complex value of type {} is {}", foundDt.getName(), validateAndUpdate);
311
312         return validateAndUpdate.right.booleanValue();
313
314     }
315
316
317     public StorageOperationStatus validateAndUpdateAttribute(final AttributeDataDefinition attributeDefinition,
318                                                              final Map<String, DataTypeDefinition> dataTypes) {
319
320         log.trace("Going to validate attribute type and value. {}", attributeDefinition);
321
322         final String attributeDefinitionType = attributeDefinition.getType();
323         final String value = (String) attributeDefinition.get_default();
324
325         final ToscaPropertyType type = getType(attributeDefinitionType);
326
327         if (type == null) {
328
329             final DataTypeDefinition dataTypeDefinition = dataTypes.get(attributeDefinitionType);
330             if (dataTypeDefinition == null) {
331                 log.debug("The type {} of attribute cannot be found.", attributeDefinitionType);
332                 return StorageOperationStatus.INVALID_TYPE;
333             }
334
335             return validateAndUpdateAttributeComplexValue(attributeDefinition, attributeDefinitionType, value,
336                 dataTypeDefinition, dataTypes);
337
338         }
339         String innerType;
340
341         final Either<String, JanusGraphOperationStatus> checkInnerType = getInnerType(type, attributeDefinition::getSchema);
342         if (checkInnerType.isRight()) {
343             return StorageOperationStatus.INVALID_TYPE;
344         }
345         innerType = checkInnerType.left().value();
346
347         log.trace("After validating property type {}", attributeDefinitionType);
348
349         if (!isValidValue(type, value, innerType, dataTypes)) {
350             log.info(THE_VALUE_OF_ATTRIBUTE_FROM_TYPE_IS_INVALID, value, type);
351             return StorageOperationStatus.INVALID_VALUE;
352         }
353
354         final PropertyValueConverter converter = type.getConverter();
355
356         if (isEmptyValue(value)) {
357             log.debug("Default value was not sent for attribute {}. Set default value to {}",
358                 attributeDefinition.getName(), EMPTY_VALUE);
359             attributeDefinition.set_default(EMPTY_VALUE);
360         } else if (!isEmptyValue(value)) {
361             attributeDefinition.set_default(converter.convert(value, innerType, dataTypes));
362         }
363         return StorageOperationStatus.OK;
364     }
365
366     private StorageOperationStatus validateAndUpdateAttributeComplexValue(
367         final AttributeDataDefinition attributeDefinition,
368         final String attributeType,
369         final String value,
370         final DataTypeDefinition dataTypeDefinition,
371         final Map<String, DataTypeDefinition> dataTypes) {
372
373         final ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter
374             .validateAndUpdate(value, dataTypeDefinition, dataTypes);
375         if (!validateResult.right.booleanValue()) {
376             log.debug(THE_VALUE_OF_ATTRIBUTE_FROM_TYPE_IS_INVALID, attributeType, attributeType);
377             return StorageOperationStatus.INVALID_VALUE;
378         }
379         final JsonElement jsonElement = validateResult.left;
380         if (log.isTraceEnabled()) {
381             log.trace("Going to update value in attribute definition {} {}", attributeDefinition.getName(),
382                 (jsonElement != null ? jsonElement.toString() : null));
383         }
384         updateAttributeValue(attributeDefinition, jsonElement);
385         return StorageOperationStatus.OK;
386     }
387
388     private void updateAttributeValue(final AttributeDataDefinition attributeDefinition,
389                                       final JsonElement jsonElement) {
390         attributeDefinition.set_default(jsonElement);
391     }
392 }