Release version 1.13.7
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / operations / impl / DataTypeOperation.java
1 /*
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021 Nordix Foundation
4  *  ================================================================================
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  *
16  *  SPDX-License-Identifier: Apache-2.0
17  *  ============LICENSE_END=========================================================
18  */
19
20 package org.openecomp.sdc.be.model.operations.impl;
21
22 import fj.data.Either;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.Comparator;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Optional;
30 import org.apache.commons.collections.CollectionUtils;
31 import org.apache.commons.collections4.MapUtils;
32 import org.apache.commons.lang.StringUtils;
33 import org.apache.commons.lang3.tuple.ImmutableTriple;
34 import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
35 import org.apache.tinkerpop.gremlin.structure.Vertex;
36 import org.janusgraph.core.JanusGraph;
37 import org.openecomp.sdc.be.config.BeEcompErrorManager;
38 import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
39 import org.openecomp.sdc.be.dao.api.ActionStatus;
40 import org.openecomp.sdc.be.dao.janusgraph.HealingJanusGraphGenericDao;
41 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
42 import org.openecomp.sdc.be.dao.janusgraph.QueryType;
43 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
44 import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
45 import org.openecomp.sdc.be.datatypes.elements.DataTypeDataDefinition;
46 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
47 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
48 import org.openecomp.sdc.be.exception.supplier.DataTypeOperationExceptionSupplier;
49 import org.openecomp.sdc.be.model.DataTypeDefinition;
50 import org.openecomp.sdc.be.model.PropertyDefinition;
51 import org.openecomp.sdc.be.model.dto.PropertyDefinitionDto;
52 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.exception.OperationException;
53 import org.openecomp.sdc.be.model.mapper.PropertyDefinitionDtoMapper;
54 import org.openecomp.sdc.be.model.normatives.ElementTypeEnum;
55 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
56 import org.openecomp.sdc.be.resources.data.DataTypeData;
57 import org.openecomp.sdc.be.resources.data.PropertyData;
58 import org.openecomp.sdc.common.log.enums.EcompLoggerErrorCode;
59 import org.slf4j.Logger;
60 import org.slf4j.LoggerFactory;
61 import org.springframework.beans.factory.annotation.Autowired;
62 import org.springframework.stereotype.Component;
63
64 @Component("dataType-operation")
65 public class DataTypeOperation extends AbstractOperation {
66
67     private static final Logger LOGGER = LoggerFactory.getLogger(DataTypeOperation.class);
68
69     private ModelOperation modelOperation;
70     private PropertyOperation propertyOperation;
71
72     @Autowired
73     public DataTypeOperation(final HealingJanusGraphGenericDao janusGraphGenericDao) {
74         this.janusGraphGenericDao = janusGraphGenericDao;
75     }
76
77     //circular dependency ModelOperation->ModelElementOperation->DataTypeOperation
78     @Autowired
79     public void setModelOperation(final ModelOperation modelOperation) {
80         this.modelOperation = modelOperation;
81     }
82
83     @Autowired
84     public void setPropertyOperation(PropertyOperation propertyOperation) {
85         this.propertyOperation = propertyOperation;
86     }
87
88     public List<DataTypeData> getAllDataTypeNodes() {
89         final List<DataTypeData> dataTypesFound = new ArrayList<>();
90         final Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypesWithNullModel =
91             janusGraphGenericDao.getByCriteria(NodeTypeEnum.DataType, null, DataTypeData.class);
92
93         final var dataTypesValidated = validateDataType(getAllDataTypesWithNullModel, null);
94         if (CollectionUtils.isNotEmpty(dataTypesValidated)) {
95             dataTypesFound.addAll(dataTypesValidated);
96         }
97
98         final List<DataTypeData> allDataTypeNodesWithModel = getAllDataTypesWithModel();
99         if (CollectionUtils.isNotEmpty(allDataTypeNodesWithModel)) {
100             dataTypesFound.addAll(allDataTypeNodesWithModel);
101         }
102         return dataTypesFound;
103     }
104
105     public Map<String, List<String>> getAllDataTypeUidsToModels() {
106         final Map<String, List<String>> dataTypesFound = new HashMap<>();
107         final Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypesWithNullModel =
108             janusGraphGenericDao.getByCriteria(NodeTypeEnum.DataType, null, DataTypeData.class);
109
110         final var dataTypesValidated = validateDataType(getAllDataTypesWithNullModel, null);
111
112         for (DataTypeData dataType : dataTypesValidated) {
113             if (!dataTypesFound.containsKey(dataType.getUniqueId())) {
114                 dataTypesFound.put(dataType.getUniqueId(), new ArrayList<>());
115             }
116             dataTypesFound.get(dataType.getUniqueId()).add(null);
117         }
118
119         modelOperation.findAllModels()
120             .forEach(model -> {
121                 for (DataTypeData dataType : getAllDataTypesWithModel(model.getName())) {
122                     if (!dataTypesFound.containsKey(dataType.getUniqueId())) {
123                         dataTypesFound.put(dataType.getUniqueId(), new ArrayList<>());
124                     }
125                     dataTypesFound.get(dataType.getUniqueId()).add(model.getName());
126                 }
127             });
128         return dataTypesFound;
129     }
130
131     public List<String> getAllDataTypeModels(final String dataTypeName) {
132         final List<String> models = new ArrayList<>();
133         ImmutableTriple<QueryType, String, Object> criteria =
134             new ImmutableTriple<>(QueryType.HAS, GraphPropertiesDictionary.NAME.getProperty(), dataTypeName);
135
136         final Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypesForModel =
137             janusGraphGenericDao.getByCriteria(NodeTypeEnum.DataType, DataTypeData.class, List.of(criteria));
138         final var dataTypesValidated = validateDataType(getAllDataTypesForModel, null);
139         for (DataTypeData dataType : dataTypesValidated) {
140             models.add(dataType.getDataTypeDataDefinition().getModel());
141         }
142         return models;
143     }
144
145     private List<DataTypeData> getAllDataTypesWithModel(final String modelName) {
146         final Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypesByModel = janusGraphGenericDao
147             .getByCriteriaForModel(NodeTypeEnum.DataType, null, modelName, DataTypeData.class);
148         return validateDataType(getAllDataTypesByModel, modelName);
149     }
150
151     private List<DataTypeData> getAllDataTypesWithModel() {
152         final List<DataTypeData> dataTypesWithModel = new ArrayList<>();
153         modelOperation.findAllModels()
154             .forEach(model -> {
155                 final var modelName = model.getName();
156                 final Either<List<DataTypeData>, JanusGraphOperationStatus> getAllDataTypesByModel = janusGraphGenericDao
157                     .getByCriteriaForModel(NodeTypeEnum.DataType, null, modelName, DataTypeData.class);
158                 final var dataTypesValidated = validateDataType(getAllDataTypesByModel, modelName);
159                 dataTypesWithModel.addAll(dataTypesValidated);
160             });
161         return dataTypesWithModel;
162     }
163
164     private List<DataTypeData> validateDataType(final Either<List<DataTypeData>, JanusGraphOperationStatus> getDataTypes, final String modelName) {
165         if (getDataTypes.isRight() && getDataTypes.right().value() == JanusGraphOperationStatus.NOT_FOUND) {
166             return Collections.emptyList();
167         }
168         if (getDataTypes.isRight()) {
169             final var status = getDataTypes.right().value();
170             if (LOGGER.isErrorEnabled()) {
171                 final var errorMsg = String.format("Failed to fetch data types from database with model %s. Status is %s", modelName, status);
172                 LOGGER.error(String.valueOf(EcompLoggerErrorCode.UNKNOWN_ERROR), DataTypeOperation.class.getName(), errorMsg);
173                 BeEcompErrorManager.getInstance().logInternalConnectionError(DataTypeOperation.class.getName(), errorMsg, ErrorSeverity.ERROR);
174             }
175             return Collections.emptyList();
176         }
177         return getDataTypes.left().value();
178     }
179
180     public void deleteDataTypesByModelId(final String modelId) {
181         final JanusGraph janusGraph = janusGraphGenericDao.getJanusGraph();
182         final GraphTraversalSource traversal = janusGraph.traversal();
183         final List<Vertex> dataTypeList = traversal.V()
184             .has(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), modelId)
185             .out(GraphEdgeLabels.MODEL_ELEMENT.getProperty())
186             .has(GraphPropertiesDictionary.LABEL.getProperty(), NodeTypeEnum.DataType.getName())
187             .toList();
188         dataTypeList.forEach(dataTypeVertex -> {
189             traversal.V(dataTypeVertex).out(GraphEdgeLabels.PROPERTY.getProperty()).drop().iterate();
190             dataTypeVertex.remove();
191         });
192     }
193
194     public void deleteDataTypesByDataTypeId(final String dataTypeId) {
195         final JanusGraph janusGraph = janusGraphGenericDao.getJanusGraph();
196         final GraphTraversalSource traversal = janusGraph.traversal();
197         final List<Vertex> dataTypeList = traversal.V()
198             .has(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), dataTypeId)
199             .toList();
200         dataTypeList.forEach(dataTypeVertex -> {
201             traversal.V(dataTypeVertex).out(GraphEdgeLabels.PROPERTY.getProperty()).drop().iterate();
202             dataTypeVertex.remove();
203         });
204     }
205
206     public Optional<DataTypeDataDefinition> getDataTypeByUid(final String uniqueId) {
207         final Either<DataTypeData, JanusGraphOperationStatus> dataTypeEither = janusGraphGenericDao
208             .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.DataType), uniqueId, DataTypeData.class);
209         if (dataTypeEither.isRight()) {
210             if (JanusGraphOperationStatus.NOT_FOUND.equals(dataTypeEither.right().value())) {
211                 return Optional.empty();
212             }
213             final StorageOperationStatus storageOperationStatus
214                 = DaoStatusConverter.convertJanusGraphStatusToStorageStatus(dataTypeEither.right().value());
215             LOGGER.warn("Failed to fetch data type '{}' from JanusGraph. Status is: {}", uniqueId, storageOperationStatus);
216             throw new OperationException(ActionStatus.GENERAL_ERROR,
217                 String.format("Failed to fetch data type '%s' from JanusGraph. Status is: %s", uniqueId, storageOperationStatus));
218         }
219         return Optional.of(dataTypeEither.left().value().getDataTypeDataDefinition());
220     }
221
222     public Optional<DataTypeDataDefinition> getDataTypeByNameAndModel(final String name, String model) {
223         final Either<DataTypeData, JanusGraphOperationStatus> dataTypeEither = janusGraphGenericDao
224             .getNode("name", name, DataTypeData.class, model);
225         if (dataTypeEither.isRight()) {
226             if (JanusGraphOperationStatus.NOT_FOUND.equals(dataTypeEither.right().value())) {
227                 return Optional.empty();
228             }
229             final StorageOperationStatus storageOperationStatus
230                 = DaoStatusConverter.convertJanusGraphStatusToStorageStatus(dataTypeEither.right().value());
231             LOGGER.warn("Failed to fetch data type '{}' from JanusGraph. Status is: {}", name, storageOperationStatus);
232             throw new OperationException(ActionStatus.GENERAL_ERROR,
233                 String.format("Failed to fetch data type '%s' from JanusGraph. Status is: %s", name, storageOperationStatus));
234         }
235         return Optional.of(dataTypeEither.left().value().getDataTypeDataDefinition());
236     }
237
238     public List<PropertyDefinition> findAllProperties(final String uniqueId) {
239         final Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> propertiesEither =
240             propertyOperation.findPropertiesOfNode(NodeTypeEnum.DataType, uniqueId);
241         if (propertiesEither.isRight()) {
242             final JanusGraphOperationStatus status = propertiesEither.right().value();
243             if (status == JanusGraphOperationStatus.NOT_FOUND) {
244                 return List.of();
245             }
246             LOGGER.error("Could not retrieve data type '{}' properties. JanusGraphOperationStatus: '{}'", uniqueId, status);
247
248             throw DataTypeOperationExceptionSupplier.unexpectedErrorWhileFetchingProperties(uniqueId).get();
249         }
250         final Map<String, PropertyDefinition> propertyMap = propertiesEither.left().value();
251         if (MapUtils.isEmpty(propertyMap)) {
252             return List.of();
253         }
254         final List<PropertyDefinition> propertyDefinitions = new ArrayList<>(propertyMap.values());
255         propertyDefinitions.sort(Comparator.comparing(PropertyDefinition::getName));
256         return propertyDefinitions;
257     }
258
259     public Optional<DataTypeDefinition> handleDataTypeDownloadRequestById(final String dataTypeId) {
260         if (StringUtils.isNotEmpty(dataTypeId)) {
261             Optional<DataTypeDataDefinition> dataTypeDataDefinition = getDataTypeByUid(dataTypeId);
262             if (dataTypeDataDefinition.isPresent()) {
263                 DataTypeDefinition dataTypeDefinition = new DataTypeDefinition(dataTypeDataDefinition.get());
264                 dataTypeDefinition.setProperties(findAllProperties(dataTypeId));
265                 return Optional.of(dataTypeDefinition);
266             }
267         }
268         return Optional.empty();
269     }
270
271     public PropertyDefinitionDto createProperty(final String dataTypeId, final PropertyDefinitionDto propertyDefinitionDto) {
272         final String propertyName = propertyDefinitionDto.getName();
273         LOGGER.debug("Adding property '{}' to data type '{}'.", propertyName, dataTypeId);
274
275         getDataTypeByUid(dataTypeId).orElseThrow(DataTypeOperationExceptionSupplier.dataTypeNotFound(dataTypeId));
276
277         final Either<PropertyData, JanusGraphOperationStatus> resultEither =
278             propertyOperation.addPropertyToNodeType(propertyName, PropertyDefinitionDtoMapper.mapTo(propertyDefinitionDto),
279                 NodeTypeEnum.DataType, dataTypeId, false);
280         if (resultEither.isRight()) {
281             final JanusGraphOperationStatus status = resultEither.right().value();
282             LOGGER.debug("Could not create property '{}' on data type '{}'. JanusGraph status is '{}'", propertyName, dataTypeId, status);
283             if (status == JanusGraphOperationStatus.JANUSGRAPH_SCHEMA_VIOLATION) {
284                 throw DataTypeOperationExceptionSupplier.dataTypePropertyAlreadyExists(dataTypeId, propertyName).get();
285             }
286             LOGGER.error("Could not create property '{}' on data type '{}'. JanusGraph status is '{}'", propertyName, dataTypeId, status);
287             throw DataTypeOperationExceptionSupplier.unexpectedErrorWhileCreatingProperty(dataTypeId, propertyName).get();
288         }
289         LOGGER.debug("Property '{}' was added to data type '{}'.", propertyName, dataTypeId);
290         final PropertyData propertyData = resultEither.left().value();
291         final PropertyDataDefinition propertyDataDefinition = propertyData.getPropertyDataDefinition();
292         propertyDataDefinition.setName(propertyName);
293         propertyDataDefinition.setPropertyConstraints(propertyData.getConstraints());
294         return PropertyDefinitionDtoMapper.mapFrom(propertyDataDefinition);
295     }
296
297     public PropertyDefinitionDto updateProperty(final String dataTypeId, final PropertyDefinitionDto propertyDefinitionDto) {
298         final String propertyName = propertyDefinitionDto.getName();
299         LOGGER.debug("Updating property '{}' to data type '{}'.", propertyName, dataTypeId);
300
301         getDataTypeByUid(dataTypeId).orElseThrow(DataTypeOperationExceptionSupplier.dataTypeNotFound(dataTypeId));
302
303         final Either<PropertyDefinition, JanusGraphOperationStatus> resultEither =
304             propertyOperation.updatePropertyAssociatedToNode(NodeTypeEnum.DataType, dataTypeId,
305                 PropertyDefinitionDtoMapper.mapTo(propertyDefinitionDto));
306         if (resultEither.isRight()) {
307             final JanusGraphOperationStatus status = resultEither.right().value();
308             LOGGER.debug("Could not update property '{}' on data type '{}'. JanusGraph status is '{}'", propertyName, dataTypeId, status);
309             if (status == JanusGraphOperationStatus.JANUSGRAPH_SCHEMA_VIOLATION) {
310                 throw DataTypeOperationExceptionSupplier.dataTypePropertyAlreadyExists(dataTypeId, propertyName).get();
311             }
312             LOGGER.error("Could not update property '{}' on data type '{}'. JanusGraph status is '{}'", propertyName, dataTypeId, status);
313             throw DataTypeOperationExceptionSupplier.unexpectedErrorWhileCreatingProperty(dataTypeId, propertyName).get();
314         }
315         LOGGER.debug("Property '{}' was updated in data type '{}'.", propertyName, dataTypeId);
316         final PropertyDefinition propertyData = resultEither.left().value();
317         return PropertyDefinitionDtoMapper.mapFrom(propertyData);
318     }
319
320     public void updatePropertyInAdditionalTypeDataType(final DataTypeDataDefinition dataTypeDataDefinition,
321                                                        final PropertyDefinitionDto property,
322                                                        final boolean isAdd) {
323         modelOperation.updatePropertyInAdditionalType(ElementTypeEnum.DATA_TYPE, property, dataTypeDataDefinition.getModel(),
324             dataTypeDataDefinition.getName(), isAdd);
325     }
326
327     public void removeDataTypeFromAdditionalType(final DataTypeDataDefinition dataTypeDataDefinition) {
328         modelOperation.removeTypeFromAdditionalType(ElementTypeEnum.DATA_TYPE, dataTypeDataDefinition.getModel(),
329             dataTypeDataDefinition.getName());
330     }
331
332     public PropertyDefinitionDto deleteProperty(final DataTypeDataDefinition dataTypeDataDefinition, final String propertyId) {
333         final List<PropertyDefinition> propertiesData = findAllProperties(dataTypeDataDefinition.getUniqueId());
334         final String dataTypeDataDefinitionName = dataTypeDataDefinition.getName();
335         if (CollectionUtils.isEmpty(propertiesData)) {
336             throw new OperationException(ActionStatus.PROPERTY_NOT_FOUND,
337                 String.format("Failed to find property '%s' for data type '%s'", propertyId, dataTypeDataDefinitionName));
338         }
339         Optional<PropertyDefinition> optionalPropertyDefinition = propertiesData.stream()
340             .filter(propertyDataDefinition -> propertyDataDefinition.getUniqueId().equals(propertyId)).findAny();
341         optionalPropertyDefinition.orElseThrow(() -> {
342             throw new OperationException(ActionStatus.PROPERTY_NOT_FOUND,
343                 String.format("Failed to find property '%s' for data type '%s'", propertyId, dataTypeDataDefinitionName));
344         });
345         final Either<PropertyData, JanusGraphOperationStatus> statusEither = propertyOperation.deletePropertyFromGraph(propertyId);
346         if (statusEither.isRight()) {
347             throw new OperationException(ActionStatus.PROPERTY_NOT_FOUND,
348                 String.format("Failed to delete property '%s' from data type '%s'", propertyId, dataTypeDataDefinitionName));
349         }
350         final PropertyDefinition propertyDefinition = optionalPropertyDefinition.get();
351         final PropertyData propertyData = statusEither.left().value();
352         final PropertyDataDefinition propertyDataDefinition = propertyData.getPropertyDataDefinition();
353         propertyDataDefinition.setName(propertyDefinition.getName());
354         propertyDataDefinition.setPropertyConstraints(propertyData.getConstraints());
355         propertiesData.remove(propertyDefinition);
356         return PropertyDefinitionDtoMapper.mapFrom(propertyDataDefinition);
357     }
358 }