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