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.ObjectMapper;
25 import com.google.gson.Gson;
26 import java.util.Collections;
27 import java.util.HashMap;
28 import java.util.List;
30 import java.util.Map.Entry;
31 import java.util.Objects;
33 import java.util.stream.Collectors;
34 import org.apache.commons.collections.MapUtils;
35 import org.openecomp.sdc.be.datatypes.elements.InputDataDefinition;
36 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
37 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
38 import org.openecomp.sdc.be.model.Component;
39 import org.openecomp.sdc.be.model.ComponentInstance;
40 import org.openecomp.sdc.be.model.DataTypeDefinition;
41 import org.openecomp.sdc.be.model.InterfaceDefinition;
42 import org.openecomp.sdc.be.model.Product;
43 import org.openecomp.sdc.be.model.PropertyDefinition;
44 import org.openecomp.sdc.be.tosca.PropertyConvertor.PropertyType;
45 import org.openecomp.sdc.be.tosca.model.ToscaInput;
46 import org.openecomp.sdc.be.tosca.model.ToscaInterfaceDefinition;
47 import org.openecomp.sdc.be.tosca.model.ToscaInterfaceNodeType;
48 import org.openecomp.sdc.be.tosca.model.ToscaLifecycleOperationDefinition;
49 import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
50 import org.openecomp.sdc.be.tosca.model.ToscaProperty;
51 import org.openecomp.sdc.be.tosca.utils.OperationArtifactUtil;
52 import org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum;
53 import org.openecomp.sdc.tosca.datatypes.ToscaFunctions;
54 import org.springframework.beans.factory.annotation.Autowired;
55 import org.springframework.stereotype.Service;
58 public class InterfacesOperationsConverter {
61 private static final String DERIVED_FROM_STANDARD_INTERFACE = "tosca.interfaces.node.lifecycle.Standard";
62 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";
68 public static final String SELF = "SELF";
69 private static final String LOCAL_INTERFACE_TYPE = "Local";
71 private final PropertyConvertor propertyConvertor;
74 public InterfacesOperationsConverter(final PropertyConvertor propertyConvertor) {
75 this.propertyConvertor = propertyConvertor;
79 * Creates the interface_types element.
81 * @param component to work on
82 * @return the added element
84 public static Map<String, Object> addInterfaceTypeElement(Component component, List<String> allInterfaceTypes) {
85 if (component instanceof Product) {
88 final Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
89 if (MapUtils.isEmpty(interfaces)) {
93 Map<String, Object> toscaInterfaceTypes = new HashMap<>();
94 for (InterfaceDefinition interfaceDefinition : interfaces.values()) {
95 boolean isInterfaceTypeExistInGlobalType =
96 allInterfaceTypes.stream().anyMatch(type -> type.equalsIgnoreCase(interfaceDefinition.getType()));
97 if (!isInterfaceTypeExistInGlobalType) {
98 ToscaInterfaceNodeType toscaInterfaceType = new ToscaInterfaceNodeType();
99 toscaInterfaceType.setDerived_from(DERIVED_FROM_STANDARD_INTERFACE);
101 final Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
102 Map<String, Object> toscaOperations = new HashMap<>();
104 for (Map.Entry<String, OperationDataDefinition> operationEntry : operations.entrySet()) {
105 toscaOperations.put(operationEntry.getValue().getName(), null);
107 toscaInterfaceType.setOperations(toscaOperations);
108 Map<String, Object> interfacesAsMap = getObjectAsMap(toscaInterfaceType);
109 Map<String, Object> operationsMap = (Map<String, Object>) interfacesAsMap.remove(OPERATIONS.getElementName());
110 interfacesAsMap.putAll(operationsMap);
112 toscaInterfaceTypes.put(getInterfaceType(component, LOCAL_INTERFACE_TYPE), interfacesAsMap);
115 return MapUtils.isNotEmpty(toscaInterfaceTypes) ? toscaInterfaceTypes : null;
119 * Adds the 'interfaces' element to the node type provided.
121 * @param component to work on
122 * @param nodeType to which the interfaces element will be added
124 public void addInterfaceDefinitionElement(Component component, ToscaNodeType nodeType,
125 Map<String, DataTypeDefinition> dataTypes,
126 boolean isAssociatedComponent) {
127 if (component instanceof Product) {
130 final Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
131 if (MapUtils.isEmpty(interfaces)) {
134 Map<String, Object> toscaInterfaceDefinitions = getInterfacesMap(component, dataTypes,
135 isAssociatedComponent);
136 if (MapUtils.isNotEmpty(toscaInterfaceDefinitions)) {
137 nodeType.setInterfaces(toscaInterfaceDefinitions);
141 private Map<String, Object> getInterfacesMap(Component component,
142 Map<String, DataTypeDefinition> dataTypes,
143 boolean isAssociatedComponent) {
144 return getInterfacesMap(component, null, component.getInterfaces(), dataTypes, isAssociatedComponent, false);
147 public Map<String, Object> getInterfacesMap(final Component component,
148 final ComponentInstance componentInstance,
149 final Map<String, InterfaceDefinition> interfaces,
150 final Map<String, DataTypeDefinition> dataTypes,
151 final boolean isAssociatedComponent,
152 final boolean isServiceProxyInterface) {
153 if(MapUtils.isEmpty(interfaces)) {
157 Map<String, Object> toscaInterfaceDefinitions = new HashMap<>();
158 for (InterfaceDefinition interfaceDefinition : interfaces.values()) {
159 ToscaInterfaceDefinition toscaInterfaceDefinition = new ToscaInterfaceDefinition();
160 final String interfaceType;
161 if(componentInstance != null && LOCAL_INTERFACE_TYPE.equals(interfaceDefinition.getType())) {
162 interfaceType = DERIVED_FROM_BASE_DEFAULT + componentInstance.getSourceModelName();
164 interfaceType = getInterfaceType(component, interfaceDefinition.getType());
166 if (componentInstance == null) {
167 toscaInterfaceDefinition.setType(interfaceType);
169 toscaInterfaceDefinition.setType(interfaceType);
170 final Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
171 Map<String, Object> toscaOperationMap = new HashMap<>();
173 String operationArtifactPath;
174 for (Map.Entry<String, OperationDataDefinition> operationEntry : operations.entrySet()) {
175 ToscaLifecycleOperationDefinition toscaOperation = new ToscaLifecycleOperationDefinition();
176 if (isArtifactPresent(operationEntry)) {
177 operationArtifactPath = OperationArtifactUtil
178 .createOperationArtifactPath(component, componentInstance, operationEntry.getValue(),
179 isAssociatedComponent);
180 toscaOperation.setImplementation(operationArtifactPath);
182 toscaOperation.setDescription(operationEntry.getValue().getDescription());
183 fillToscaOperationInputs(operationEntry.getValue(), dataTypes, toscaOperation, isServiceProxyInterface);
185 toscaOperationMap.put(operationEntry.getValue().getName(), toscaOperation);
187 toscaInterfaceDefinition.setOperations(toscaOperationMap);
189 final Map<String, Object> interfaceInputMap = createInterfaceInputMap(interfaceDefinition, dataTypes);
190 if (!interfaceInputMap.isEmpty()) {
191 toscaInterfaceDefinition.setInputs(interfaceInputMap);
194 Map<String, Object> interfaceDefAsMap = getObjectAsMap(toscaInterfaceDefinition);
195 if (interfaceDefAsMap.containsKey(INPUTS.getElementName())) {
196 handleDefaults((Map<String, Object>) interfaceDefAsMap.get(INPUTS.getElementName()));
198 Map<String, Object> operationsMap = (Map<String, Object>) interfaceDefAsMap.remove(OPERATIONS.getElementName());
199 if (isServiceProxyInterface) {
200 //Remove input type and copy default value directly into the proxy node template from the node type
201 handleServiceProxyOperationInputValue(operationsMap, interfaceType);
203 handleDefaults(operationsMap);
205 interfaceDefAsMap.putAll(operationsMap);
206 toscaInterfaceDefinitions.put(getLastPartOfName(interfaceType), interfaceDefAsMap);
209 return toscaInterfaceDefinitions;
212 public void removeInterfacesWithoutOperations(final Map<String, Object> interfaceMap) {
213 if (MapUtils.isEmpty(interfaceMap)) {
216 final Set<String> emptyInterfaces = interfaceMap.entrySet().stream()
218 final Object value = entry.getValue();
219 if (value instanceof ToscaInterfaceDefinition) {
220 final ToscaInterfaceDefinition interfaceDefinition = (ToscaInterfaceDefinition) value;
221 return MapUtils.isEmpty(interfaceDefinition.getOperations());
222 } else if (value instanceof Map) {
223 final Map<String, Object> interfaceDefMap = (Map<String, Object>) value;
224 return MapUtils.isEmpty(interfaceDefMap);
227 }).map(Entry::getKey).collect(Collectors.toSet());
228 emptyInterfaces.forEach(interfaceMap::remove);
231 private Map<String, Object> createInterfaceInputMap(final InterfaceDefinition interfaceDefinition,
232 final Map<String, DataTypeDefinition> allDataTypeMap) {
233 final Map<String, InputDataDefinition> inputMap = interfaceDefinition.getInputs();
234 if (MapUtils.isEmpty(inputMap)) {
235 return Collections.emptyMap();
237 final Map<String, Object> toscaInterfaceInputMap = new HashMap<>();
238 for (final Entry<String, InputDataDefinition> inputEntry : inputMap.entrySet()) {
239 final InputDataDefinition inputDataDefinition = inputEntry.getValue();
240 final ToscaProperty toscaProperty = propertyConvertor
241 .convertProperty(allDataTypeMap, new PropertyDefinition(inputDataDefinition), PropertyType.INPUT);
242 toscaInterfaceInputMap.put(inputEntry.getKey(), new ToscaInput(toscaProperty));
244 return toscaInterfaceInputMap;
247 private static void handleServiceProxyOperationInputValue(Map<String, Object> operationsMap, String parentKey) {
248 for (Map.Entry<String, Object> operationEntry : operationsMap.entrySet()) {
249 final Object value = operationEntry.getValue();
250 final String key = operationEntry.getKey();
251 if (value instanceof Map) {
252 if ("inputs".equals(parentKey)) {
253 Object defaultValue = getDefaultValue((Map<String, Object>) value);
254 operationsMap.put(key, defaultValue);
256 handleServiceProxyOperationInputValue((Map<String, Object>) value, key);
262 private static Object getDefaultValue(Map<String, Object> inputValueMap) {
263 Object defaultValue = null;
264 for (Map.Entry<String, Object> operationEntry : inputValueMap.entrySet()) {
265 final Object value = operationEntry.getValue();
266 if (value instanceof Map) {
267 getDefaultValue((Map<String, Object>) value);
269 final String key = operationEntry.getKey();
270 if (key.equals(DEFAULTP)) {
271 defaultValue = inputValueMap.remove(key);
278 * workaround for : currently "defaultp" is not being converted to "default" by the relevant code in
279 * ToscaExportHandler so, any string Map key named "defaultp" will have its named changed to "default"
280 * @param operationsMap the map to update
282 private void handleDefaults(Map<String, Object> operationsMap) {
283 for (Map.Entry<String, Object> operationEntry : operationsMap.entrySet()) {
284 final Object value = operationEntry.getValue();
285 if (value instanceof Map) {
286 handleDefaults((Map<String, Object>) value);
288 final String key = operationEntry.getKey();
289 if (key.equals(DEFAULTP)) {
290 Object removed = operationsMap.remove(key);
291 operationsMap.put(ToscaTagNamesEnum.DEFAULT.getElementName(), removed);
296 private static String getLastPartOfName(String toscaResourceName) {
297 return toscaResourceName.substring(toscaResourceName.lastIndexOf(DOT) + 1);
300 private static boolean isArtifactPresent(Map.Entry<String, OperationDataDefinition> operationEntry) {
301 final boolean isImplementationPresent = !Objects.isNull(operationEntry.getValue().getImplementation());
302 if (isImplementationPresent) {
303 return !Objects.isNull(operationEntry.getValue().getImplementation().getArtifactName());
308 private void fillToscaOperationInputs(OperationDataDefinition operation,
309 Map<String, DataTypeDefinition> dataTypes,
310 ToscaLifecycleOperationDefinition toscaOperation,
311 boolean isServiceProxyInterface) {
312 if (Objects.isNull(operation.getInputs()) || operation.getInputs().isEmpty()) {
313 toscaOperation.setInputs(null);
316 Map<String, ToscaProperty> toscaInputs = new HashMap<>();
318 for (OperationInputDefinition input : operation.getInputs().getListToscaDataDefinition()) {
319 ToscaProperty toscaInput = new ToscaProperty();
320 toscaInput.setDescription(input.getDescription());
321 toscaInput.setType(input.getType());
322 toscaInput.setRequired(input.isRequired());
323 if (isServiceProxyInterface) {
324 String inputValue = Objects.nonNull(input.getValue()) ? getInputValue(input.getValue()) :
325 getInputValue(input.getToscaDefaultValue());
326 toscaInput.setDefaultp(propertyConvertor.convertToToscaObject(input, inputValue, dataTypes, false));
328 toscaInput.setDefaultp(propertyConvertor
329 .convertToToscaObject(input, getInputValue(input.getToscaDefaultValue()),
332 toscaInputs.put(input.getName(), toscaInput);
334 toscaOperation.setInputs(toscaInputs);
337 private static String getInputValue(String inputValue) {
338 String toscaInputValue = inputValue;
339 if (Objects.nonNull(inputValue) && inputValue.contains(ToscaFunctions.GET_OPERATION_OUTPUT.getFunctionName())) {
340 Gson gson = new Gson();
341 Map<String, List<String>> consumptionValue = gson.fromJson(inputValue, Map.class);
342 List<String> mappedOutputValue =
343 consumptionValue.get(ToscaFunctions.GET_OPERATION_OUTPUT.getFunctionName());
344 //Extract the interface name from the interface type
345 String interfaceType = mappedOutputValue.get(1);
346 String interfaceName = interfaceType.substring(interfaceType.lastIndexOf('.') + 1);
347 mappedOutputValue.remove(1);
348 mappedOutputValue.add(1, interfaceName);
349 toscaInputValue = gson.toJson(consumptionValue);
351 return toscaInputValue;
354 private static String getInterfaceType(Component component, String interfaceType) {
355 if (LOCAL_INTERFACE_TYPE.equals(interfaceType)) {
356 return DERIVED_FROM_BASE_DEFAULT
357 + component.getComponentMetadataDefinition()
358 .getMetadataDataDefinition().getSystemName();
361 return interfaceType;
364 private static Map<String, Object> getObjectAsMap(Object obj) {
365 ObjectMapper objectMapper = new ObjectMapper();
366 if (obj instanceof ToscaInterfaceDefinition) {
367 //Prevent empty field serialization in interface definition
368 objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
370 Map<String, Object> objectAsMap =
371 obj instanceof Map ? (Map<String, Object>) obj : objectMapper.convertValue(obj, Map.class);
373 final String defaultEntry = DEFAULT.getElementName();
374 if (objectAsMap.containsKey(defaultEntry)) {
375 objectAsMap.put(DEFAULT_HAS_UNDERSCORE, objectAsMap.remove(defaultEntry));