Upgrade SDC from Titan to Janus Graph
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / CommonImportManager.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  * Modifications copyright (c) 2019 Nokia
20  * ================================================================================
21  */
22
23 package org.openecomp.sdc.be.components.impl;
24
25 import fj.data.Either;
26 import org.apache.commons.lang3.tuple.ImmutablePair;
27 import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
28 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
29 import org.openecomp.sdc.be.components.impl.model.ToscaTypeImportData;
30 import org.openecomp.sdc.be.config.BeEcompErrorManager;
31 import org.openecomp.sdc.be.dao.api.ActionStatus;
32 import org.openecomp.sdc.be.datatypes.elements.ToscaTypeDataDefinition;
33 import org.openecomp.sdc.be.datatypes.tosca.ToscaDataDefinition;
34 import org.openecomp.sdc.be.impl.ComponentsUtils;
35 import org.openecomp.sdc.be.model.*;
36 import org.openecomp.sdc.be.model.normatives.ToscaTypeMetadata;
37 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
38 import org.openecomp.sdc.be.model.operations.api.TypeOperations;
39 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation;
40 import org.openecomp.sdc.common.log.wrappers.Logger;
41 import org.openecomp.sdc.exception.ResponseFormat;
42 import org.springframework.stereotype.Component;
43 import org.yaml.snakeyaml.Yaml;
44
45 import java.util.*;
46 import java.util.Map.Entry;
47 import java.util.function.BiFunction;
48 import java.util.function.Consumer;
49 import java.util.function.Function;
50 import java.util.stream.Collectors;
51
52 import static java.util.stream.Collectors.toList;
53
54 @Component("commonImportManager")
55 public class CommonImportManager {
56
57     private static final Logger log = Logger.getLogger(CommonImportManager.class.getName());
58
59     private final ComponentsUtils componentsUtils;
60     private final PropertyOperation propertyOperation;
61
62     public CommonImportManager(ComponentsUtils componentsUtils, PropertyOperation propertyOperation) {
63         this.componentsUtils = componentsUtils;
64         this.propertyOperation = propertyOperation;
65     }
66
67     public static void setProperties(Map<String, Object> toscaJson, Consumer<List<PropertyDefinition>> consumer) {
68         consumer.accept(getProperties(toscaJson));
69     }
70
71     private static List<PropertyDefinition> getProperties(Map<String, Object> toscaJson) {
72         List<PropertyDefinition> values = null;
73         Either<Map<String, PropertyDefinition>, ResultStatusEnum> properties = ImportUtils.getProperties(toscaJson);
74
75         if (properties.isLeft()) {
76             values = new ArrayList<>();
77             Map<String, PropertyDefinition> propertiesMap = properties.left().value();
78             if (propertiesMap != null && !propertiesMap.isEmpty()) {
79
80                 for (Entry<String, PropertyDefinition> entry : propertiesMap.entrySet()) {
81                     String propName = entry.getKey();
82                     PropertyDefinition propertyDefinition = entry.getValue();
83                     PropertyDefinition newPropertyDefinition = new PropertyDefinition(propertyDefinition);
84                     newPropertyDefinition.setName(propName);
85                     values.add(newPropertyDefinition);
86                 }
87             }
88         }
89
90         return values;
91     }
92
93     protected void setPropertiesMap(Map<String, Object> toscaJson, Consumer<Map<String, PropertyDefinition>> consumer) {
94         final List<PropertyDefinition> properties = getProperties(toscaJson);
95         if (properties != null) {
96             Map<String, PropertyDefinition> collect = properties.stream()
97                                                         .collect(Collectors.toMap(PropertyDefinition::getName, Function.identity()));
98             consumer.accept(collect);
99         }
100     }
101
102     public interface ICreateElementType<T1, T2, T3> {
103         T3 createElement(T1 firstArg, T2 secondArg);
104     }
105
106     protected <T> Either<List<T>, ActionStatus> createElementTypesFromYml(String elementTypesYml, ICreateElementType<String, Map<String, Object>, T> createApi) {
107
108         List<T> elementTypes;
109         Map<String, Object> toscaJson = convertToFieldMap(elementTypesYml);
110         if (toscaJson==null) {
111             return Either.right(ActionStatus.INVALID_YAML_FILE);
112         }
113         elementTypes = createElementTypesFromToscaJsonMap(createApi, toscaJson);
114         return Either.left(elementTypes);
115     }
116
117     @SuppressWarnings("unchecked")
118     private Map<String, Object> convertToFieldMap(String elementTypesYml) {
119         Map<String, Object> toscaJson = null;
120         try {
121             toscaJson = (Map<String, Object>) new Yaml().load(elementTypesYml);
122         } catch (Exception e) {
123             log.debug("Failed to yaml file {}", elementTypesYml, e);
124         }
125         return toscaJson;
126     }
127
128
129     protected <T extends ToscaDataDefinition> List<T> createTypesFromToscaJsonMap(
130             BiFunction<String, Map<String, Object>, T> createApi, Map<String, Object> toscaJson) {
131         List<T> elementTypes = new ArrayList<>();
132
133         for (Entry<String, Object> elementTypeNameDataEntry : toscaJson.entrySet()) {
134             String elementTypeName = elementTypeNameDataEntry.getKey();
135             Map<String, Object> elementTypeJsonData = (Map<String, Object>) elementTypeNameDataEntry.getValue();
136             T elementDefinition = createApi.apply(elementTypeName, elementTypeJsonData);
137             elementTypes.add(elementDefinition);
138         }
139         return elementTypes;
140     }
141
142     protected <T> List<T> createElementTypesFromToscaJsonMap(
143                 ICreateElementType<String, Map<String, Object>, T> createApi, Map<String, Object> toscaJson) {
144         List<T> elementTypes = new ArrayList<>();
145
146         for (Entry<String, Object> elementTypeNameDataEntry : toscaJson.entrySet()) {
147             String elementTypeName = elementTypeNameDataEntry.getKey();
148             Map<String, Object> elementTypeJsonData = (Map<String, Object>) elementTypeNameDataEntry.getValue();
149             T elementDefinition = createApi.createElement(elementTypeName, elementTypeJsonData);
150             elementTypes.add(elementDefinition);
151         }
152         return elementTypes;
153     }
154
155     protected <T> Map<String, T> createElementTypesMapFromToscaJsonMap(
156                 ICreateElementType<String, Map<String, Object>, T> createApi, Map<String, Object> toscaJson) {
157         Map<String, T> elementTypesMap = new HashMap<>();
158         
159         Iterator<Entry<String, Object>> elementTypesEntryItr = toscaJson.entrySet().iterator();
160         while (elementTypesEntryItr.hasNext()) {
161             Entry<String, Object> elementTypeNameDataEntry = elementTypesEntryItr.next();
162             String elementTypeName = elementTypeNameDataEntry.getKey();
163             Map<String, Object> elementTypeJsonData = (Map<String, Object>) elementTypeNameDataEntry.getValue();
164             T elementDefinition = createApi.createElement(elementTypeName, elementTypeJsonData);
165             elementTypesMap.put(elementTypeName, elementDefinition);
166         }
167         return elementTypesMap;
168     }
169
170     protected <F> void setField(Map<String, Object> toscaJson, String fieldName, Consumer<F> setter) {
171         if (toscaJson.containsKey(fieldName)) {
172             F fieldValue = (F) toscaJson.get(fieldName);
173             setter.accept(fieldValue);
174         }
175     }
176
177     public enum ElementTypeEnum {
178         POLICY_TYPE, GROUP_TYPE, DATA_TYPE, CAPABILITY_TYPE, INTERFACE_LIFECYCLE_TYPE, RELATIONSHIP_TYPE
179     }
180
181     private ActionStatus convertFromStorageResponseForElementType(StorageOperationStatus status, ElementTypeEnum elementTypeEnum) {
182         ActionStatus ret;
183         switch (elementTypeEnum) {
184         case GROUP_TYPE:
185             ret = componentsUtils.convertFromStorageResponseForGroupType(status);
186             break;
187         case DATA_TYPE:
188             ret = componentsUtils.convertFromStorageResponseForDataType(status);
189             break;
190         case CAPABILITY_TYPE:
191             ret = componentsUtils.convertFromStorageResponseForCapabilityType(status);
192             break;
193         case INTERFACE_LIFECYCLE_TYPE:
194             ret = componentsUtils.convertFromStorageResponseForLifecycleType(status);
195             break;
196         case RELATIONSHIP_TYPE:
197             ret = componentsUtils.convertFromStorageResponseForRelationshipType(status);
198             break;
199         default:
200             ret = componentsUtils.convertFromStorageResponse(status);
201             break;
202         }
203         return ret;
204     }
205
206     private <T> ResponseFormat getResponseFormatForElementType(ActionStatus actionStatus, ElementTypeEnum elementTypeEnum, T elementTypeDefinition) {
207         ResponseFormat ret;
208         switch (elementTypeEnum) {
209         case GROUP_TYPE:
210             ret = componentsUtils.getResponseFormatByGroupType(actionStatus, (GroupTypeDefinition) elementTypeDefinition);
211             break;
212         case POLICY_TYPE:
213             ret = componentsUtils.getResponseFormatByPolicyType(actionStatus, (PolicyTypeDefinition) elementTypeDefinition);
214             break;
215         case DATA_TYPE:
216             ret = componentsUtils.getResponseFormatByDataType(actionStatus, (DataTypeDefinition) elementTypeDefinition, null);
217             break;
218         case CAPABILITY_TYPE:
219             ret = componentsUtils.getResponseFormatByCapabilityType(actionStatus, (CapabilityTypeDefinition) elementTypeDefinition);
220             break;
221
222         default:
223             ret = componentsUtils.getResponseFormat(actionStatus);
224             break;
225         }
226         return ret;
227     }
228
229     private <T extends ToscaDataDefinition> List<ImmutablePair<T, Boolean>> createTypesByDao(List<T> elementTypesToCreate,
230                                                                                              TypeOperations<T> typeOperations) {
231         List<ImmutablePair<T, Boolean>> createdElementTypes = new ArrayList<>();
232         for (T newTypeDefinition : elementTypesToCreate) {
233             try {
234                 String typeName = newTypeDefinition.getType();
235                 T existingDefinition = typeOperations.getLatestType(typeName);
236                 if (existingDefinition == null /*new type*/) {
237                     typeOperations.addType(newTypeDefinition);
238                 } else {
239                     if (typeOperations.isSameType(newTypeDefinition, existingDefinition)) {
240                         propertyOperation.getJanusGraphGenericDao().rollback();
241                         createdElementTypes.add(new ImmutablePair<>(newTypeDefinition, null));
242                         continue;
243                     } else {
244                         typeOperations.updateType(existingDefinition, newTypeDefinition);
245                     }
246                 }
247                 propertyOperation.getJanusGraphGenericDao().commit();
248                 createdElementTypes.add(new ImmutablePair<>(newTypeDefinition, true));
249             } catch (Exception e) {
250                 propertyOperation.getJanusGraphGenericDao().rollback();
251                 createdElementTypes.add(new ImmutablePair<>(newTypeDefinition, false));
252             }
253
254         }
255         return createdElementTypes;
256     }
257     
258     protected <T> Either<List<ImmutablePair<T, Boolean>>, ResponseFormat> createElementTypesByDao(List<T> elementTypesToCreate,
259             Function<T, Either<ActionStatus, ResponseFormat>> validator, Function<T, ImmutablePair<ElementTypeEnum, String>> elementInfoGetter,
260             Function<String, Either<T, StorageOperationStatus>> elementFetcher, Function<T, Either<T, StorageOperationStatus>> elementAdder,
261             BiFunction<T, T, Either<T, StorageOperationStatus>> elementUpgrader) {
262
263         List<ImmutablePair<T, Boolean>> createdElementTypes = new ArrayList<>();
264
265         Either<List<ImmutablePair<T, Boolean>>, ResponseFormat> eitherResult = Either.left(createdElementTypes);
266         Iterator<T> elementTypeItr = elementTypesToCreate.iterator();
267
268         try {
269             while (elementTypeItr.hasNext()) {
270                 T elementType = elementTypeItr.next();
271                 eitherResult = handleType(elementType, validator, elementInfoGetter, elementFetcher, elementAdder, elementUpgrader)
272                                             .left()
273                                             .map(elem -> append(createdElementTypes, elem));
274                 
275                 if (eitherResult.isRight()) {
276                     break;
277                 }
278                 
279                 if(!elementTypeItr.hasNext()) {
280                     log.info("all {} were created successfully!!!", elementType);
281                 }
282             }
283         }
284         catch(Exception e) {
285             eitherResult = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
286             throw e;
287         } 
288         finally {
289             if (eitherResult.isLeft()) {
290                 propertyOperation.getJanusGraphGenericDao().commit();
291             }
292             else {
293                 propertyOperation.getJanusGraphGenericDao().rollback();
294             }
295         }
296
297         return eitherResult;
298     }
299     
300     private static <T> List<T> append(List<T> list, T value) {
301         list.add(value);
302         return list;
303     }
304     
305     
306     private <T> Either<ImmutablePair<T, Boolean>, ResponseFormat> handleType(T elementType, 
307             Function<T, Either<ActionStatus, ResponseFormat>> validator, Function<T, ImmutablePair<ElementTypeEnum, String>> elementInfoGetter,
308             Function<String, Either<T, StorageOperationStatus>> elementFetcher, Function<T, Either<T, StorageOperationStatus>> elementAdder,
309             BiFunction<T, T, Either<T, StorageOperationStatus>> elementUpgrader) {
310         
311         final ImmutablePair<ElementTypeEnum, String> elementInfo = elementInfoGetter.apply(elementType);
312         ElementTypeEnum elementTypeEnum = elementInfo.left;
313         String elementName = elementInfo.right;
314         
315         Either<ActionStatus, ResponseFormat> validateElementType = validator.apply(elementType);
316         if (validateElementType.isRight()) {
317             ResponseFormat responseFormat = validateElementType.right().value();
318             log.debug("Failed in validation of element type: {}. Response is {}", elementType, responseFormat.getFormattedMessage());
319             return Either.right(responseFormat);
320         }
321
322         log.info("send {} : {} to dao for create", elementTypeEnum, elementName);
323
324         Either<T, StorageOperationStatus> findElementType = elementFetcher.apply(elementName);
325         if (findElementType.isRight()) {
326             StorageOperationStatus status = findElementType.right().value();
327             log.debug("searched {} finished with result:{}", elementTypeEnum, status);
328             if (status != StorageOperationStatus.NOT_FOUND) {
329                 ResponseFormat responseFormat = getResponseFormatForElementType(convertFromStorageResponseForElementType(status, elementTypeEnum), elementTypeEnum, elementType);
330                 return Either.right(responseFormat);
331             } else {
332                 return addElementType(elementType, elementAdder, elementTypeEnum, elementName);
333             }
334         } else {
335
336             if (elementUpgrader != null) {
337                 return updateElementType(elementType, elementUpgrader, elementTypeEnum, elementName, findElementType.left().value());
338
339             } else {
340                 // mshitrit Once GroupType Versions are supported add
341                 // code here
342                 log.debug("{} : {} already exists.", elementTypeEnum, elementName);
343                 return Either.left(new ImmutablePair<>(elementType, false));
344             }
345
346         }
347     }
348
349     private <T> Either<ImmutablePair<T, Boolean>, ResponseFormat> addElementType(T elementType, Function<T, Either<T, StorageOperationStatus>> elementAdder, ElementTypeEnum elementTypeEnum, String elementName) {
350         Either<T, StorageOperationStatus> dataModelResponse = elementAdder.apply(elementType);
351         
352         if (dataModelResponse.isRight()) {
353             BeEcompErrorManager.getInstance().logBeFailedAddingNodeTypeError("Create {}", elementTypeEnum.name());
354             log.debug("failed to create {}: {}", elementTypeEnum, elementName);
355             if (dataModelResponse.right().value() != StorageOperationStatus.OK) {
356                 ResponseFormat responseFormat = getResponseFormatForElementType(convertFromStorageResponseForElementType(dataModelResponse.right().value(), elementTypeEnum), elementTypeEnum, elementType);
357                 
358                 return Either.right(responseFormat);
359             } else {
360                 return Either.left(new ImmutablePair<>(elementType, false));
361             }
362         } else {
363             log.debug("{} : {}  was created successfully.", elementTypeEnum, elementName);
364             return Either.left(new ImmutablePair<>(elementType, true));
365         }
366     }
367     
368     
369     private <T> Either<ImmutablePair<T, Boolean>, ResponseFormat> updateElementType(T elementType, BiFunction<T, T, Either<T, StorageOperationStatus>> elementUpgrader, 
370                                                         ElementTypeEnum elementTypeEnum, String elementName, T existingElementType) {
371         Either<T, StorageOperationStatus> upgradeResponse = elementUpgrader.apply(elementType, existingElementType);
372         if (upgradeResponse.isRight()) {
373             StorageOperationStatus status = upgradeResponse.right().value();
374             if (status == StorageOperationStatus.OK) {
375                 return Either.left(new ImmutablePair<>(elementType, false));
376             } else {
377                 ResponseFormat responseFormat = getResponseFormatForElementType(convertFromStorageResponseForElementType(upgradeResponse.right().value(), elementTypeEnum), elementTypeEnum, elementType);
378                 return Either.right(responseFormat);
379             }
380         } else {
381             log.debug("{} : {}  was upgraded successfully.", elementTypeEnum, elementName);
382             return Either.left(new ImmutablePair<>(elementType, true));
383         }
384     }
385
386     
387     public <T extends ToscaTypeDataDefinition> Either<List<ImmutablePair<T, Boolean>>, ResponseFormat> createElementTypes(ToscaTypeImportData toscaTypeImportData, Function<String, Either<List<T>, ActionStatus>> elementTypeFromYmlCreater,
388                                                                                                                           Function<List<T>, Either<List<ImmutablePair<T, Boolean>>, ResponseFormat>> elementTypeDaoCreater) {
389         Either<List<T>, ActionStatus> elementTypes = elementTypeFromYmlCreater.apply(toscaTypeImportData.getToscaTypesYml());
390         return elementTypes
391                 .right()
392                 .map(err -> componentsUtils.getResponseFormat(err, ""))
393                 .left()
394                 .map(toscaTypes -> enrichTypesWithNonToscaMetadata(toscaTypes, toscaTypeImportData.getToscaTypeMetadata()))
395                 .left()
396                 .bind(elementTypeDaoCreater::apply);
397     }
398
399     public <T extends ToscaDataDefinition> List<ImmutablePair<T, Boolean>> createElementTypes(String toscaTypesYml,
400                                                                                               BiFunction<String, Map<String, Object>, T> createApi,
401                                                                                               TypeOperations<T> typeOperations) {
402         Map<String, Object> fieldMap = convertToFieldMap(toscaTypesYml);
403         if (fieldMap==null) {
404             throw new ByActionStatusComponentException(ActionStatus.INVALID_YAML_FILE);
405         }
406         List<T> elementTypes = createTypesFromToscaJsonMap(createApi, fieldMap);
407         return createTypesByDao(elementTypes, typeOperations);
408     }
409
410     private <T extends ToscaTypeDataDefinition> List<T> enrichTypesWithNonToscaMetadata(List<T> toscaTypes, Map<String, ToscaTypeMetadata> toscaTypeMetadata) {
411         return toscaTypes.stream()
412                   .map(toscaType -> setNonToscaMetaDataOnType(toscaTypeMetadata, toscaType))
413                   .collect(toList());
414     }
415
416     private <T extends ToscaTypeDataDefinition> T setNonToscaMetaDataOnType(Map<String, ToscaTypeMetadata> toscaTypeMetadata, T toscaTypeDefinition) {
417         String toscaType = toscaTypeDefinition.getType();
418         ToscaTypeMetadata typeMetaData = toscaTypeMetadata.get(toscaType);
419         if (typeMetaData == null) {
420             log.debug("failing while trying to associate metadata for type {}. type not exist", toscaType);
421             throw new ByActionStatusComponentException(ActionStatus.GENERAL_ERROR);
422         }
423         toscaTypeDefinition.setIcon(typeMetaData.getIcon());
424         toscaTypeDefinition.setName(typeMetaData.getDisplayName());
425         return toscaTypeDefinition;
426     }
427
428     public <T> Either<List<ImmutablePair<T, Boolean>>, ResponseFormat> createElementTypes(String elementTypesYml, Function<String, Either<List<T>, ActionStatus>> elementTypeFromYmlCreater,
429             Function<List<T>, Either<List<ImmutablePair<T, Boolean>>, ResponseFormat>> elementTypeDaoCreater, ElementTypeEnum elementTypeEnum) {
430
431         Either<List<T>, ActionStatus> elementTypes = elementTypeFromYmlCreater.apply(elementTypesYml);
432         if (elementTypes.isRight()) {
433             ActionStatus status = elementTypes.right().value();
434             ResponseFormat responseFormat = getResponseFormatForElementType(status, elementTypeEnum, null);
435             return Either.right(responseFormat);
436         }
437         return elementTypeDaoCreater.apply(elementTypes.left().value());
438
439     }
440 }