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