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