3650422afcf14ac102e126bff37a860bd78e7c7e
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / tosca / InterfacesOperationsConverter.java
1 /*
2  * Copyright © 2016-2020 European Support Limited
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
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 package org.openecomp.sdc.be.tosca;
17
18 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.DEFAULT;
19 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.INPUTS;
20 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.OPERATIONS;
21
22 import com.fasterxml.jackson.annotation.JsonInclude;
23 import com.fasterxml.jackson.databind.DeserializationFeature;
24 import com.fasterxml.jackson.databind.ObjectMapper;
25 import com.fasterxml.jackson.databind.module.SimpleModule;
26 import com.google.gson.Gson;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Map.Entry;
32 import java.util.Objects;
33 import java.util.Set;
34 import java.util.stream.Collectors;
35 import org.apache.commons.collections.MapUtils;
36 import org.apache.commons.collections4.CollectionUtils;
37 import org.apache.commons.lang3.StringUtils;
38 import org.apache.commons.lang3.math.NumberUtils;
39 import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
40 import org.openecomp.sdc.be.datatypes.elements.InputDataDefinition;
41 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
42 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
43 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
44 import org.openecomp.sdc.be.model.Component;
45 import org.openecomp.sdc.be.model.ComponentInstance;
46 import org.openecomp.sdc.be.model.DataTypeDefinition;
47 import org.openecomp.sdc.be.model.InterfaceDefinition;
48 import org.openecomp.sdc.be.model.Product;
49 import org.openecomp.sdc.be.model.PropertyDefinition;
50 import org.openecomp.sdc.be.tosca.PropertyConvertor.PropertyType;
51 import org.openecomp.sdc.be.tosca.model.ToscaArtifactDefinition;
52 import org.openecomp.sdc.be.tosca.model.ToscaInput;
53 import org.openecomp.sdc.be.tosca.model.ToscaInterfaceDefinition;
54 import org.openecomp.sdc.be.tosca.model.ToscaInterfaceNodeType;
55 import org.openecomp.sdc.be.tosca.model.ToscaInterfaceOperationImplementation;
56 import org.openecomp.sdc.be.tosca.model.ToscaLifecycleOperationDefinition;
57 import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
58 import org.openecomp.sdc.be.tosca.model.ToscaProperty;
59 import org.openecomp.sdc.be.tosca.model.ToscaPropertyAssignment;
60 import org.openecomp.sdc.be.tosca.model.ToscaPropertyAssignmentJsonSerializer;
61 import org.openecomp.sdc.be.tosca.utils.OperationArtifactUtil;
62 import org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum;
63 import org.openecomp.sdc.tosca.datatypes.ToscaFunctions;
64 import org.springframework.beans.factory.annotation.Autowired;
65 import org.springframework.stereotype.Service;
66
67 @Service
68 public class InterfacesOperationsConverter {
69
70     public static final String SELF = "SELF";
71     private static final String DERIVED_FROM_STANDARD_INTERFACE = "tosca.interfaces.node.lifecycle.Standard";
72     private static final String DERIVED_FROM_BASE_DEFAULT = "org.openecomp.interfaces.node.lifecycle.";
73     private static final String DEFAULT_HAS_UNDERSCORE = "_default";
74     private static final String DOT = ".";
75     private static final String DEFAULTP = "defaultp";
76     private static final String LOCAL_INTERFACE_TYPE = "Local";
77     private final PropertyConvertor propertyConvertor;
78
79     @Autowired
80     public InterfacesOperationsConverter(final PropertyConvertor propertyConvertor) {
81         this.propertyConvertor = propertyConvertor;
82     }
83
84     /**
85      * Creates the interface_types element.
86      *
87      * @param component to work on
88      * @return the added element
89      */
90     public Map<String, Object> addInterfaceTypeElement(Component component, List<String> allInterfaceTypes) {
91         if (component instanceof Product) {
92             return null;
93         }
94         final Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
95         if (MapUtils.isEmpty(interfaces)) {
96             return null;
97         }
98         Map<String, Object> toscaInterfaceTypes = new HashMap<>();
99         for (InterfaceDefinition interfaceDefinition : interfaces.values()) {
100             boolean isInterfaceTypeExistInGlobalType = allInterfaceTypes.stream()
101                 .anyMatch(type -> type.equalsIgnoreCase(interfaceDefinition.getType()));
102             if (!isInterfaceTypeExistInGlobalType) {
103                 ToscaInterfaceNodeType toscaInterfaceType = new ToscaInterfaceNodeType();
104                 toscaInterfaceType.setDerived_from(DERIVED_FROM_STANDARD_INTERFACE);
105                 final Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
106                 Map<String, Object> toscaOperations = new HashMap<>();
107                 for (Map.Entry<String, OperationDataDefinition> operationEntry : operations.entrySet()) {
108                     toscaOperations.put(operationEntry.getValue().getName(), null);
109                 }
110                 toscaInterfaceType.setOperations(toscaOperations);
111                 Map<String, Object> interfacesAsMap = getObjectAsMap(toscaInterfaceType);
112                 Map<String, Object> operationsMap = (Map<String, Object>) interfacesAsMap.remove(OPERATIONS.getElementName());
113                 interfacesAsMap.putAll(operationsMap);
114                 toscaInterfaceTypes.put(getInterfaceType(component, LOCAL_INTERFACE_TYPE), interfacesAsMap);
115             }
116         }
117         return MapUtils.isNotEmpty(toscaInterfaceTypes) ? toscaInterfaceTypes : null;
118     }
119
120     private static Object getDefaultValue(Map<String, Object> inputValueMap) {
121         Object defaultValue = null;
122         for (Map.Entry<String, Object> operationEntry : inputValueMap.entrySet()) {
123             final Object value = operationEntry.getValue();
124             if (value instanceof Map) {
125                 getDefaultValue((Map<String, Object>) value);
126             }
127             final String key = operationEntry.getKey();
128             if (key.equals(DEFAULTP)) {
129                 defaultValue = inputValueMap.remove(key);
130             }
131         }
132         return defaultValue;
133     }
134
135     //Remove input type and copy default value directly into the proxy node template from the node type
136     private static void handleOperationInputValue(Map<String, Object> operationsMap, String parentKey) {
137         for (Map.Entry<String, Object> operationEntry : operationsMap.entrySet()) {
138             final Object value = operationEntry.getValue();
139             final String key = operationEntry.getKey();
140             if (value instanceof Map) {
141                 if (INPUTS.getElementName().equals(parentKey)) {
142                     Object defaultValue = getDefaultValue((Map<String, Object>) value);
143                     operationsMap.put(key, defaultValue);
144                 } else {
145                     handleOperationInputValue((Map<String, Object>) value, key);
146                 }
147             }
148         }
149     }
150
151     private static String getLastPartOfName(String toscaResourceName) {
152         return toscaResourceName.substring(toscaResourceName.lastIndexOf(DOT) + 1);
153     }
154
155     private boolean isArtifactPresent(final OperationDataDefinition operationDataDefinition) {
156         return operationDataDefinition.getImplementation() != null
157             && StringUtils.isNotEmpty(operationDataDefinition.getImplementation().getArtifactName());
158     }
159
160     private static String getInputValue(final OperationInputDefinition input) {
161         String inputValue = input.getValue() == null ? input.getToscaDefaultValue() : input.getValue();
162         if (inputValue != null && inputValue.contains(ToscaFunctions.GET_OPERATION_OUTPUT.getFunctionName())) {
163             Gson gson = new Gson();
164             Map<String, List<String>> consumptionValue = gson.fromJson(inputValue, Map.class);
165             List<String> mappedOutputValue = consumptionValue.get(ToscaFunctions.GET_OPERATION_OUTPUT.getFunctionName());
166             //Extract the interface name from the interface type
167             String interfaceType = mappedOutputValue.get(1);
168             String interfaceName = interfaceType.substring(interfaceType.lastIndexOf('.') + 1);
169             mappedOutputValue.remove(1);
170             mappedOutputValue.add(1, interfaceName);
171             inputValue = gson.toJson(consumptionValue);
172         }
173         return inputValue;
174     }
175
176     private static String getInterfaceType(Component component, String interfaceType) {
177         if (LOCAL_INTERFACE_TYPE.equals(interfaceType)) {
178             return DERIVED_FROM_BASE_DEFAULT + component.getComponentMetadataDefinition().getMetadataDataDefinition().getSystemName();
179         }
180         return interfaceType;
181     }
182
183     private static Map<String, Object> getObjectAsMap(final Object obj) {
184         final Map<String, Object> objectAsMap;
185         if (obj instanceof Map) {
186             objectAsMap = (Map<String, Object>) obj;
187         } else {
188             final ObjectMapper objectMapper = new ObjectMapper();
189             final SimpleModule module = new SimpleModule("ToscaPropertyAssignmentSerializer");
190             module.addSerializer(ToscaPropertyAssignment.class, new ToscaPropertyAssignmentJsonSerializer());
191             objectMapper.registerModule(module);
192             if (obj instanceof ToscaInterfaceDefinition) {
193                 //Prevent empty field serialization in interface definition
194                 objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
195             }
196             objectAsMap = objectMapper.convertValue(obj, Map.class);
197         }
198
199         final String defaultEntry = DEFAULT.getElementName();
200         if (objectAsMap.containsKey(defaultEntry)) {
201             objectAsMap.put(DEFAULT_HAS_UNDERSCORE, objectAsMap.remove(defaultEntry));
202         }
203         return objectAsMap;
204     }
205
206     /**
207      * Adds the 'interfaces' element to the node type provided.
208      *
209      * @param component to work on
210      * @param nodeType  to which the interfaces element will be added
211      */
212     public void addInterfaceDefinitionElement(Component component, ToscaNodeType nodeType, Map<String, DataTypeDefinition> dataTypes,
213                                               boolean isAssociatedComponent) {
214         if (component instanceof Product) {
215             return;
216         }
217         final Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
218         if (MapUtils.isEmpty(interfaces)) {
219             return;
220         }
221         Map<String, Object> toscaInterfaceDefinitions = getInterfacesMap(component, dataTypes, isAssociatedComponent);
222         if (MapUtils.isNotEmpty(toscaInterfaceDefinitions)) {
223             nodeType.setInterfaces(toscaInterfaceDefinitions);
224         }
225     }
226
227     private Map<String, Object> getInterfacesMap(Component component, Map<String, DataTypeDefinition> dataTypes, boolean isAssociatedComponent) {
228         return getInterfacesMap(component, null, component.getInterfaces(), dataTypes, isAssociatedComponent);
229     }
230
231     public Map<String, Object> getInterfacesMap(final Component component, final ComponentInstance componentInstance,
232                                                 final Map<String, InterfaceDefinition> interfaces, final Map<String, DataTypeDefinition> dataTypes,
233                                                 final boolean isAssociatedComponent) {
234         if (MapUtils.isEmpty(interfaces)) {
235             return null;
236         }
237         final Map<String, Object> toscaInterfaceDefinitions = new HashMap<>();
238         for (InterfaceDefinition interfaceDefinition : interfaces.values()) {
239             handleInterfaceOperations(component, componentInstance, dataTypes, isAssociatedComponent,
240                 toscaInterfaceDefinitions, interfaceDefinition);
241         }
242         return toscaInterfaceDefinitions;
243     }
244
245     public Map<String, Object> getInterfacesMapFromComponentInstance(final Component component, final ComponentInstance componentInstance,
246                                                                      final Map<String, DataTypeDefinition> dataTypes,
247                                                                      final boolean isAssociatedComponent) {
248         final Map<String, Object> toscaInterfaceDefinitions = new HashMap<>();
249         final ObjectMapper objectMapper = new ObjectMapper();
250         objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
251         for (final Map.Entry<String, Object> interfaceEntry : componentInstance.getInterfaces().entrySet()) {
252             final InterfaceDefinition interfaceDefinition = objectMapper.convertValue(interfaceEntry.getValue(), InterfaceDefinition.class);
253             handleInterfaceOperations(component, componentInstance, dataTypes, isAssociatedComponent,
254                 toscaInterfaceDefinitions, interfaceDefinition);
255         }
256         return toscaInterfaceDefinitions;
257     }
258
259     private void handleInterfaceOperations(final Component component, final ComponentInstance componentInstance,
260                                            final Map<String, DataTypeDefinition> dataTypes, final boolean isAssociatedComponent,
261                                            final Map<String, Object> toscaInterfaceDefinitions,
262                                            final InterfaceDefinition interfaceDefinition) {
263         final String interfaceType;
264         if (componentInstance != null && LOCAL_INTERFACE_TYPE.equals(interfaceDefinition.getType())) {
265             interfaceType = DERIVED_FROM_BASE_DEFAULT + componentInstance.getSourceModelName();
266         } else {
267             interfaceType = getInterfaceType(component, interfaceDefinition.getType());
268         }
269         final ToscaInterfaceDefinition toscaInterfaceDefinition = new ToscaInterfaceDefinition();
270         if (componentInstance == null) {
271             toscaInterfaceDefinition.setType(interfaceType);
272         }
273         final Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
274         final Map<String, Object> toscaOperationMap = new HashMap<>();
275         for (final Entry<String, OperationDataDefinition> operationEntry : operations.entrySet()) {
276             if (operationHasAnImplementation(operationEntry.getValue())) {
277                 final ToscaLifecycleOperationDefinition toscaLifecycleOperationDefinition = new ToscaLifecycleOperationDefinition();
278                 handleInterfaceOperationImplementation(component, componentInstance, isAssociatedComponent, operationEntry.getValue(),
279                     toscaLifecycleOperationDefinition, dataTypes);
280                 if (StringUtils.isNotEmpty(operationEntry.getValue().getDescription())) {
281                     toscaLifecycleOperationDefinition.setDescription(operationEntry.getValue().getDescription());
282                 }
283                 fillToscaOperationInputs(operationEntry.getValue(), dataTypes, toscaLifecycleOperationDefinition);
284                 toscaOperationMap.put(operationEntry.getValue().getName(), toscaLifecycleOperationDefinition);
285             }
286         }
287         toscaInterfaceDefinition.setOperations(toscaOperationMap);
288         final Map<String, Object> interfaceInputMap = createInterfaceInputMap(interfaceDefinition, dataTypes);
289         if (MapUtils.isNotEmpty(interfaceInputMap)) {
290             toscaInterfaceDefinition.setInputs(interfaceInputMap);
291         }
292         final Map<String, Object> interfaceDefinitionAsMap = getObjectAsMap(toscaInterfaceDefinition);
293         if (interfaceDefinitionAsMap.containsKey(INPUTS.getElementName())) {
294             handleDefaults((Map<String, Object>) interfaceDefinitionAsMap.get(INPUTS.getElementName()));
295         }
296         final Map<String, Object> operationsMap = (Map<String, Object>) interfaceDefinitionAsMap.remove(OPERATIONS.getElementName());
297         handleOperationInputValue(operationsMap, interfaceType);
298         interfaceDefinitionAsMap.putAll(operationsMap);
299         toscaInterfaceDefinitions.put(getLastPartOfName(interfaceType), interfaceDefinitionAsMap);
300     }
301     
302     private boolean operationHasAnImplementation(OperationDataDefinition operation) {
303         return operation.getImplementation() != null && StringUtils.isNotEmpty(operation.getImplementation().getArtifactName()) && !operation.getImplementation().getArtifactName().equals("''");
304     }
305
306     private void handleInterfaceOperationImplementation(final Component component, final ComponentInstance componentInstance,
307                                                         final boolean isAssociatedComponent,
308                                                         final OperationDataDefinition operationDataDefinition,
309                                                         final ToscaLifecycleOperationDefinition toscaOperation,
310                                                         final Map<String, DataTypeDefinition> dataTypes) {
311         final ArtifactDataDefinition implementation = operationDataDefinition.getImplementation();
312         if (implementation == null) {
313             return;
314         }
315
316         if (isArtifactPresent(operationDataDefinition)) {
317             final String operationArtifactPath =
318                 OperationArtifactUtil.createOperationArtifactPath(component, componentInstance, operationDataDefinition, isAssociatedComponent);
319             if (implementation.getArtifactType() != null) {
320                 final ToscaArtifactDefinition toscaArtifactDefinition = new ToscaArtifactDefinition();
321                 toscaArtifactDefinition.setFile(operationArtifactPath);
322                 final String artifactVersion = implementation.getArtifactVersion();
323                 toscaArtifactDefinition.setArtifact_version(!artifactVersion.equals(NumberUtils.INTEGER_ZERO.toString()) ? artifactVersion : null);
324                 toscaArtifactDefinition.setType(implementation.getArtifactType());
325                 final Map<String, ToscaPropertyAssignment> propertiesMap = handleImplementationProperties(operationDataDefinition, dataTypes);
326                 if (MapUtils.isNotEmpty(propertiesMap)) {
327                     toscaArtifactDefinition.setProperties(propertiesMap);
328                 }
329                 final ToscaInterfaceOperationImplementation toscaInterfaceOperationImplementation = new ToscaInterfaceOperationImplementation();
330                 toscaInterfaceOperationImplementation.setPrimary(toscaArtifactDefinition);
331                 toscaOperation.setImplementation(toscaInterfaceOperationImplementation);
332             } else {
333                 toscaOperation.setImplementation(
334                     StringUtils.isBlank(operationArtifactPath) || "null".equals(operationArtifactPath) ? null : operationArtifactPath);
335             }
336         }
337     }
338
339     private Map<String, ToscaPropertyAssignment> handleImplementationProperties(final OperationDataDefinition operationDataDefinition,
340                                                                                 final Map<String, DataTypeDefinition> dataTypes) {
341         if (operationDataDefinition.getImplementation() == null) {
342             return new HashMap<>();
343         }
344
345         final List<PropertyDataDefinition> properties = operationDataDefinition.getImplementation().getProperties();
346         if (CollectionUtils.isEmpty(properties)) {
347             return new HashMap<>();
348         }
349
350         final Map<String, ToscaPropertyAssignment> propertiesMap = new HashMap<>();
351         properties.stream()
352             .filter(propertyDataDefinition -> StringUtils.isNotEmpty(propertyDataDefinition.getValue()))
353             .forEach(propertyDataDefinition -> {
354                     final String propertyValue =
355                         propertyDataDefinition.getValue() != null ? propertyDataDefinition.getValue() : propertyDataDefinition.getDefaultValue();
356                     final ToscaPropertyAssignment toscaPropertyAssignment = new ToscaPropertyAssignment();
357                     toscaPropertyAssignment.setValue(propertyConvertor.convertToToscaObject(propertyDataDefinition, propertyValue, dataTypes, false));
358                     propertiesMap.put(propertyDataDefinition.getName(), toscaPropertyAssignment);
359                 }
360             );
361
362         return propertiesMap;
363     }
364
365     public void removeInterfacesWithoutOperations(final Map<String, Object> interfaceMap) {
366         if (MapUtils.isEmpty(interfaceMap)) {
367             return;
368         }
369         final Set<String> emptyInterfaces = interfaceMap.entrySet().stream().filter(entry -> {
370             final Object value = entry.getValue();
371             if (value instanceof ToscaInterfaceDefinition) {
372                 final ToscaInterfaceDefinition interfaceDefinition = (ToscaInterfaceDefinition) value;
373                 return MapUtils.isEmpty(interfaceDefinition.getOperations());
374             } else if (value instanceof Map) {
375                 final Map<String, Object> interfaceDefMap = (Map<String, Object>) value;
376                 return MapUtils.isEmpty(interfaceDefMap);
377             }
378             return false;
379         }).map(Entry::getKey).collect(Collectors.toSet());
380         emptyInterfaces.forEach(interfaceMap::remove);
381     }
382
383     private Map<String, Object> createInterfaceInputMap(final InterfaceDefinition interfaceDefinition,
384                                                         final Map<String, DataTypeDefinition> allDataTypeMap) {
385         final Map<String, InputDataDefinition> inputMap = interfaceDefinition.getInputs();
386         if (MapUtils.isEmpty(inputMap)) {
387             return Collections.emptyMap();
388         }
389         final Map<String, Object> toscaInterfaceInputMap = new HashMap<>();
390         for (final Entry<String, InputDataDefinition> inputEntry : inputMap.entrySet()) {
391             final InputDataDefinition inputDataDefinition = inputEntry.getValue();
392             final ToscaProperty toscaProperty = propertyConvertor
393                 .convertProperty(allDataTypeMap, new PropertyDefinition(inputDataDefinition), PropertyType.INPUT);
394             toscaInterfaceInputMap.put(inputEntry.getKey(), new ToscaInput(toscaProperty));
395         }
396         return toscaInterfaceInputMap;
397     }
398
399     /*
400      * workaround for : currently "defaultp" is not being converted to "default" by the relevant code in
401      * ToscaExportHandler so, any string Map key named "defaultp" will have its named changed to "default"
402      * @param operationsMap the map to update
403      */
404     private void handleDefaults(Map<String, Object> operationsMap) {
405         for (Map.Entry<String, Object> operationEntry : operationsMap.entrySet()) {
406             final Object value = operationEntry.getValue();
407             if (value instanceof Map) {
408                 handleDefaults((Map<String, Object>) value);
409             }
410             final String key = operationEntry.getKey();
411             if (key.equals(DEFAULTP)) {
412                 Object removed = operationsMap.remove(key);
413                 operationsMap.put(ToscaTagNamesEnum.DEFAULT.getElementName(), removed);
414             }
415         }
416     }
417
418     private void fillToscaOperationInputs(OperationDataDefinition operation, Map<String, DataTypeDefinition> dataTypes,
419                                           ToscaLifecycleOperationDefinition toscaOperation) {
420         if (Objects.isNull(operation.getInputs()) || operation.getInputs().isEmpty()) {
421             toscaOperation.setInputs(null);
422             return;
423         }
424         Map<String, ToscaProperty> toscaInputs = new HashMap<>();
425         for (OperationInputDefinition input : operation.getInputs().getListToscaDataDefinition()) {
426             ToscaProperty toscaInput = new ToscaProperty();
427             toscaInput.setDescription(input.getDescription());
428             toscaInput.setType(input.getType());
429             toscaInput.setRequired(input.isRequired());
430             toscaInput.setDefaultp(propertyConvertor.convertToToscaObject(input, getInputValue(input), dataTypes, false));
431             toscaInputs.put(input.getName(), toscaInput);
432         }
433         toscaOperation.setInputs(toscaInputs);
434     }
435
436 }