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