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