2 * Copyright © 2016-2020 European Support Limited
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package org.openecomp.sdc.be.tosca;
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;
23 import com.fasterxml.jackson.annotation.JsonInclude;
24 import com.fasterxml.jackson.databind.DeserializationFeature;
25 import com.fasterxml.jackson.databind.ObjectMapper;
26 import com.google.gson.Gson;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.List;
31 import java.util.Map.Entry;
32 import java.util.Objects;
34 import java.util.stream.Collectors;
35 import org.apache.commons.collections.MapUtils;
36 import org.apache.commons.lang.StringUtils;
37 import org.openecomp.sdc.be.datatypes.elements.InputDataDefinition;
38 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
39 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
40 import org.openecomp.sdc.be.model.Component;
41 import org.openecomp.sdc.be.model.ComponentInstance;
42 import org.openecomp.sdc.be.model.DataTypeDefinition;
43 import org.openecomp.sdc.be.model.InterfaceDefinition;
44 import org.openecomp.sdc.be.model.Product;
45 import org.openecomp.sdc.be.model.PropertyDefinition;
46 import org.openecomp.sdc.be.tosca.PropertyConvertor.PropertyType;
47 import org.openecomp.sdc.be.tosca.model.ToscaInput;
48 import org.openecomp.sdc.be.tosca.model.ToscaInterfaceDefinition;
49 import org.openecomp.sdc.be.tosca.model.ToscaInterfaceNodeType;
50 import org.openecomp.sdc.be.tosca.model.ToscaLifecycleOperationDefinition;
51 import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
52 import org.openecomp.sdc.be.tosca.model.ToscaProperty;
53 import org.openecomp.sdc.be.tosca.utils.OperationArtifactUtil;
54 import org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum;
55 import org.openecomp.sdc.tosca.datatypes.ToscaFunctions;
56 import org.springframework.beans.factory.annotation.Autowired;
57 import org.springframework.stereotype.Service;
60 public class InterfacesOperationsConverter {
63 private static final String DERIVED_FROM_STANDARD_INTERFACE = "tosca.interfaces.node.lifecycle.Standard";
64 private static final String DERIVED_FROM_BASE_DEFAULT = "org.openecomp.interfaces.node.lifecycle.";
66 private static final String DEFAULT_HAS_UNDERSCORE = "_default";
67 private static final String DOT = ".";
68 private static final String DEFAULTP = "defaultp";
70 public static final String SELF = "SELF";
71 private static final String LOCAL_INTERFACE_TYPE = "Local";
73 private final PropertyConvertor propertyConvertor;
76 public InterfacesOperationsConverter(final PropertyConvertor propertyConvertor) {
77 this.propertyConvertor = propertyConvertor;
81 * Creates the interface_types element.
83 * @param component to work on
84 * @return the added element
86 public static Map<String, Object> addInterfaceTypeElement(Component component, List<String> allInterfaceTypes) {
87 if (component instanceof Product) {
90 final Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
91 if (MapUtils.isEmpty(interfaces)) {
95 Map<String, Object> toscaInterfaceTypes = new HashMap<>();
96 for (InterfaceDefinition interfaceDefinition : interfaces.values()) {
97 boolean isInterfaceTypeExistInGlobalType =
98 allInterfaceTypes.stream().anyMatch(type -> type.equalsIgnoreCase(interfaceDefinition.getType()));
99 if (!isInterfaceTypeExistInGlobalType) {
100 ToscaInterfaceNodeType toscaInterfaceType = new ToscaInterfaceNodeType();
101 toscaInterfaceType.setDerived_from(DERIVED_FROM_STANDARD_INTERFACE);
103 final Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
104 Map<String, Object> toscaOperations = new HashMap<>();
106 for (Map.Entry<String, OperationDataDefinition> operationEntry : operations.entrySet()) {
107 toscaOperations.put(operationEntry.getValue().getName(), null);
109 toscaInterfaceType.setOperations(toscaOperations);
110 Map<String, Object> interfacesAsMap = getObjectAsMap(toscaInterfaceType);
111 Map<String, Object> operationsMap = (Map<String, Object>) interfacesAsMap.remove(OPERATIONS.getElementName());
112 interfacesAsMap.putAll(operationsMap);
114 toscaInterfaceTypes.put(getInterfaceType(component, LOCAL_INTERFACE_TYPE), interfacesAsMap);
117 return MapUtils.isNotEmpty(toscaInterfaceTypes) ? toscaInterfaceTypes : null;
121 * Adds the 'interfaces' element to the node type provided.
123 * @param component to work on
124 * @param nodeType to which the interfaces element will be added
126 public void addInterfaceDefinitionElement(Component component, ToscaNodeType nodeType,
127 Map<String, DataTypeDefinition> dataTypes,
128 boolean isAssociatedComponent) {
129 if (component instanceof Product) {
132 final Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
133 if (MapUtils.isEmpty(interfaces)) {
136 Map<String, Object> toscaInterfaceDefinitions = getInterfacesMap(component, dataTypes,
137 isAssociatedComponent);
138 if (MapUtils.isNotEmpty(toscaInterfaceDefinitions)) {
139 nodeType.setInterfaces(toscaInterfaceDefinitions);
143 private Map<String, Object> getInterfacesMap(Component component,
144 Map<String, DataTypeDefinition> dataTypes,
145 boolean isAssociatedComponent) {
146 return getInterfacesMap(component, null, component.getInterfaces(), dataTypes, isAssociatedComponent, false);
149 public Map<String, Object> getInterfacesMap(final Component component,
150 final ComponentInstance componentInstance,
151 final Map<String, InterfaceDefinition> interfaces,
152 final Map<String, DataTypeDefinition> dataTypes,
153 final boolean isAssociatedComponent,
154 final boolean isServiceProxyInterface) {
155 if (MapUtils.isEmpty(interfaces)) {
159 final Map<String, Object> toscaInterfaceDefinitions = new HashMap<>();
160 for (InterfaceDefinition interfaceDefinition : interfaces.values()) {
161 handleInterfaceOperations(component, componentInstance, dataTypes, isAssociatedComponent,
162 isServiceProxyInterface, toscaInterfaceDefinitions, interfaceDefinition);
164 return toscaInterfaceDefinitions;
167 public Map<String, Object> getInterfacesMapFromComponentInstance(final Component component,
168 final ComponentInstance componentInstance,
169 final Map<String, DataTypeDefinition> dataTypes,
170 final boolean isAssociatedComponent,
171 final boolean isServiceProxyInterface) {
172 final Map<String, Object> toscaInterfaceDefinitions = new HashMap<>();
173 final ObjectMapper objectMapper = new ObjectMapper();
174 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
175 for (final Map.Entry<String, Object> interfaceEntry : componentInstance.getInterfaces().entrySet()) {
176 final InterfaceDefinition interfaceDefinition = objectMapper
177 .convertValue(interfaceEntry.getValue(), InterfaceDefinition.class);
178 handleInterfaceOperations(component, componentInstance, dataTypes, isAssociatedComponent,
179 isServiceProxyInterface, toscaInterfaceDefinitions, interfaceDefinition);
181 return toscaInterfaceDefinitions;
184 private void handleInterfaceOperations(final Component component,
185 final ComponentInstance componentInstance,
186 final Map<String, DataTypeDefinition> dataTypes,
187 final boolean isAssociatedComponent,
188 final boolean isServiceProxyInterface,
189 final Map<String, Object> toscaInterfaceDefinitions,
190 final InterfaceDefinition interfaceDefinition) {
191 final String interfaceType;
192 if (componentInstance != null && LOCAL_INTERFACE_TYPE.equals(interfaceDefinition.getType())) {
193 interfaceType = DERIVED_FROM_BASE_DEFAULT + componentInstance.getSourceModelName();
195 interfaceType = getInterfaceType(component, interfaceDefinition.getType());
197 final ToscaInterfaceDefinition toscaInterfaceDefinition = new ToscaInterfaceDefinition();
198 if (componentInstance == null) {
199 toscaInterfaceDefinition.setType(interfaceType);
201 final Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
202 final Map<String, Object> toscaOperationMap = new HashMap<>();
204 for (final Entry<String, OperationDataDefinition> operationEntry : operations.entrySet()) {
205 final ToscaLifecycleOperationDefinition toscaLifecycleOperationDefinition =
206 new ToscaLifecycleOperationDefinition();
207 handleInterfaceOperationImplementation(component, componentInstance, isAssociatedComponent,
209 toscaLifecycleOperationDefinition);
210 toscaLifecycleOperationDefinition.setDescription(operationEntry.getValue().getDescription());
211 fillToscaOperationInputs(operationEntry.getValue(), dataTypes, toscaLifecycleOperationDefinition,
212 isServiceProxyInterface);
213 toscaOperationMap.put(operationEntry.getValue().getName(), toscaLifecycleOperationDefinition);
216 toscaInterfaceDefinition.setOperations(toscaOperationMap);
217 final Map<String, Object> interfaceInputMap = createInterfaceInputMap(interfaceDefinition, dataTypes);
218 if (!interfaceInputMap.isEmpty()) {
219 toscaInterfaceDefinition.setInputs(interfaceInputMap);
221 final Map<String, Object> interfaceDefinitionAsMap = getObjectAsMap(toscaInterfaceDefinition);
222 if (interfaceDefinitionAsMap.containsKey(INPUTS.getElementName())) {
223 handleDefaults((Map<String, Object>) interfaceDefinitionAsMap.get(INPUTS.getElementName()));
225 final Map<String, Object> operationsMap =
226 (Map<String, Object>) interfaceDefinitionAsMap.remove(OPERATIONS.getElementName());
228 handleOperationInputValue(operationsMap, interfaceType);
230 interfaceDefinitionAsMap.putAll(operationsMap);
231 toscaInterfaceDefinitions.put(getLastPartOfName(interfaceType), interfaceDefinitionAsMap);
234 private void handleInterfaceOperationImplementation(final Component component,
235 final ComponentInstance componentInstance,
236 final boolean isAssociatedComponent,
237 final Entry<String, OperationDataDefinition> operationEntry,
238 final ToscaLifecycleOperationDefinition toscaOperation) {
239 final String operationArtifactPath;
240 if (isArtifactPresent(operationEntry) && StringUtils
241 .isNotEmpty(operationEntry.getValue().getImplementation().getArtifactName())) {
242 operationArtifactPath = OperationArtifactUtil
243 .createOperationArtifactPath(component, componentInstance, operationEntry.getValue(),
244 isAssociatedComponent);
245 toscaOperation.setImplementation(operationArtifactPath);
247 toscaOperation.setImplementation(operationEntry.getValue().getImplementation().getArtifactName());
251 public void removeInterfacesWithoutOperations(final Map<String, Object> interfaceMap) {
252 if (MapUtils.isEmpty(interfaceMap)) {
255 final Set<String> emptyInterfaces = interfaceMap.entrySet().stream()
257 final Object value = entry.getValue();
258 if (value instanceof ToscaInterfaceDefinition) {
259 final ToscaInterfaceDefinition interfaceDefinition = (ToscaInterfaceDefinition) value;
260 return MapUtils.isEmpty(interfaceDefinition.getOperations());
261 } else if (value instanceof Map) {
262 final Map<String, Object> interfaceDefMap = (Map<String, Object>) value;
263 return MapUtils.isEmpty(interfaceDefMap);
266 }).map(Entry::getKey).collect(Collectors.toSet());
267 emptyInterfaces.forEach(interfaceMap::remove);
270 private Map<String, Object> createInterfaceInputMap(final InterfaceDefinition interfaceDefinition,
271 final Map<String, DataTypeDefinition> allDataTypeMap) {
272 final Map<String, InputDataDefinition> inputMap = interfaceDefinition.getInputs();
273 if (MapUtils.isEmpty(inputMap)) {
274 return Collections.emptyMap();
276 final Map<String, Object> toscaInterfaceInputMap = new HashMap<>();
277 for (final Entry<String, InputDataDefinition> inputEntry : inputMap.entrySet()) {
278 final InputDataDefinition inputDataDefinition = inputEntry.getValue();
279 final ToscaProperty toscaProperty = propertyConvertor
280 .convertProperty(allDataTypeMap, new PropertyDefinition(inputDataDefinition), PropertyType.INPUT);
281 toscaInterfaceInputMap.put(inputEntry.getKey(), new ToscaInput(toscaProperty));
283 return toscaInterfaceInputMap;
286 private static Object getDefaultValue(Map<String, Object> inputValueMap) {
287 Object defaultValue = null;
288 for (Map.Entry<String, Object> operationEntry : inputValueMap.entrySet()) {
289 final Object value = operationEntry.getValue();
290 if (value instanceof Map) {
291 getDefaultValue((Map<String, Object>) value);
293 final String key = operationEntry.getKey();
294 if (key.equals(DEFAULTP)) {
295 defaultValue = inputValueMap.remove(key);
301 //Remove input type and copy default value directly into the proxy node template from the node type
302 private static void handleOperationInputValue(Map<String, Object> operationsMap, String parentKey) {
303 for (Map.Entry<String, Object> operationEntry : operationsMap.entrySet()) {
304 final Object value = operationEntry.getValue();
305 final String key = operationEntry.getKey();
306 if (value instanceof Map) {
307 if (INPUTS.getElementName().equals(parentKey)) {
308 Object defaultValue = getDefaultValue((Map<String, Object>) value);
309 operationsMap.put(key, defaultValue);
311 handleOperationInputValue((Map<String, Object>) value, key);
318 * workaround for : currently "defaultp" is not being converted to "default" by the relevant code in
319 * ToscaExportHandler so, any string Map key named "defaultp" will have its named changed to "default"
320 * @param operationsMap the map to update
322 private void handleDefaults(Map<String, Object> operationsMap) {
323 for (Map.Entry<String, Object> operationEntry : operationsMap.entrySet()) {
324 final Object value = operationEntry.getValue();
325 if (value instanceof Map) {
326 handleDefaults((Map<String, Object>) value);
328 final String key = operationEntry.getKey();
329 if (key.equals(DEFAULTP)) {
330 Object removed = operationsMap.remove(key);
331 operationsMap.put(ToscaTagNamesEnum.DEFAULT.getElementName(), removed);
336 private static String getLastPartOfName(String toscaResourceName) {
337 return toscaResourceName.substring(toscaResourceName.lastIndexOf(DOT) + 1);
340 private static boolean isArtifactPresent(Map.Entry<String, OperationDataDefinition> operationEntry) {
341 final boolean isImplementationPresent = !Objects.isNull(operationEntry.getValue().getImplementation());
342 if (isImplementationPresent) {
343 return !Objects.isNull(operationEntry.getValue().getImplementation().getArtifactName());
348 private void fillToscaOperationInputs(OperationDataDefinition operation,
349 Map<String, DataTypeDefinition> dataTypes,
350 ToscaLifecycleOperationDefinition toscaOperation,
351 boolean isServiceProxyInterface) {
352 if (Objects.isNull(operation.getInputs()) || operation.getInputs().isEmpty()) {
353 toscaOperation.setInputs(null);
356 Map<String, ToscaProperty> toscaInputs = new HashMap<>();
358 for (OperationInputDefinition input : operation.getInputs().getListToscaDataDefinition()) {
359 ToscaProperty toscaInput = new ToscaProperty();
360 toscaInput.setDescription(input.getDescription());
361 toscaInput.setType(input.getType());
362 toscaInput.setRequired(input.isRequired());
363 if (isServiceProxyInterface) {
364 String inputValue = Objects.nonNull(input.getValue()) ? getInputValue(input.getValue()) :
365 getInputValue(input.getToscaDefaultValue());
366 toscaInput.setDefaultp(propertyConvertor.convertToToscaObject(input, inputValue, dataTypes, false));
368 toscaInput.setDefaultp(propertyConvertor
369 .convertToToscaObject(input, getInputValue(input.getToscaDefaultValue()),
372 toscaInputs.put(input.getName(), toscaInput);
374 toscaOperation.setInputs(toscaInputs);
377 private static String getInputValue(String inputValue) {
378 String toscaInputValue = inputValue;
379 if (Objects.nonNull(inputValue) && inputValue.contains(ToscaFunctions.GET_OPERATION_OUTPUT.getFunctionName())) {
380 Gson gson = new Gson();
381 Map<String, List<String>> consumptionValue = gson.fromJson(inputValue, Map.class);
382 List<String> mappedOutputValue =
383 consumptionValue.get(ToscaFunctions.GET_OPERATION_OUTPUT.getFunctionName());
384 //Extract the interface name from the interface type
385 String interfaceType = mappedOutputValue.get(1);
386 String interfaceName = interfaceType.substring(interfaceType.lastIndexOf('.') + 1);
387 mappedOutputValue.remove(1);
388 mappedOutputValue.add(1, interfaceName);
389 toscaInputValue = gson.toJson(consumptionValue);
391 return toscaInputValue;
394 private static String getInterfaceType(Component component, String interfaceType) {
395 if (LOCAL_INTERFACE_TYPE.equals(interfaceType)) {
396 return DERIVED_FROM_BASE_DEFAULT
397 + component.getComponentMetadataDefinition()
398 .getMetadataDataDefinition().getSystemName();
401 return interfaceType;
404 private static Map<String, Object> getObjectAsMap(Object obj) {
405 ObjectMapper objectMapper = new ObjectMapper();
406 if (obj instanceof ToscaInterfaceDefinition) {
407 //Prevent empty field serialization in interface definition
408 objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
410 Map<String, Object> objectAsMap =
411 obj instanceof Map ? (Map<String, Object>) obj : objectMapper.convertValue(obj, Map.class);
413 final String defaultEntry = DEFAULT.getElementName();
414 if (objectAsMap.containsKey(defaultEntry)) {
415 objectAsMap.put(DEFAULT_HAS_UNDERSCORE, objectAsMap.remove(defaultEntry));