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