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