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.core.JsonProcessingException;
25 import com.fasterxml.jackson.databind.DeserializationFeature;
26 import com.fasterxml.jackson.databind.ObjectMapper;
27 import com.fasterxml.jackson.databind.module.SimpleModule;
28 import com.google.gson.Gson;
29 import java.util.Arrays;
30 import java.util.Collections;
31 import java.util.HashMap;
32 import java.util.List;
34 import java.util.Map.Entry;
35 import java.util.Objects;
36 import java.util.Optional;
38 import java.util.stream.Collectors;
39 import org.apache.commons.collections.MapUtils;
40 import org.apache.commons.collections4.CollectionUtils;
41 import org.apache.commons.lang3.StringUtils;
42 import org.apache.commons.lang3.math.NumberUtils;
43 import org.json.JSONObject;
44 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
45 import org.openecomp.sdc.be.dao.api.ActionStatus;
46 import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
47 import org.openecomp.sdc.be.datatypes.elements.InputDataDefinition;
48 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
49 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
50 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
51 import org.openecomp.sdc.be.datatypes.elements.ToscaFunction;
52 import org.openecomp.sdc.be.datatypes.elements.ToscaFunctionType;
53 import org.openecomp.sdc.be.model.Component;
54 import org.openecomp.sdc.be.model.ComponentInstance;
55 import org.openecomp.sdc.be.model.DataTypeDefinition;
56 import org.openecomp.sdc.be.model.InterfaceDefinition;
57 import org.openecomp.sdc.be.model.Product;
58 import org.openecomp.sdc.be.model.PropertyDefinition;
59 import org.openecomp.sdc.be.tosca.PropertyConvertor.PropertyType;
60 import org.openecomp.sdc.be.tosca.model.ToscaArtifactDefinition;
61 import org.openecomp.sdc.be.tosca.model.ToscaInput;
62 import org.openecomp.sdc.be.tosca.model.ToscaInterfaceDefinition;
63 import org.openecomp.sdc.be.tosca.model.ToscaInterfaceNodeType;
64 import org.openecomp.sdc.be.tosca.model.ToscaInterfaceOperationImplementation;
65 import org.openecomp.sdc.be.tosca.model.ToscaLifecycleOperationDefinition;
66 import org.openecomp.sdc.be.tosca.model.ToscaNodeType;
67 import org.openecomp.sdc.be.tosca.model.ToscaProperty;
68 import org.openecomp.sdc.be.tosca.model.ToscaPropertyAssignment;
69 import org.openecomp.sdc.be.tosca.model.ToscaPropertyAssignmentJsonSerializer;
70 import org.openecomp.sdc.be.tosca.utils.OperationArtifactUtil;
71 import org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum;
72 import org.openecomp.sdc.tosca.datatypes.ToscaFunctions;
73 import org.springframework.beans.factory.annotation.Autowired;
74 import org.springframework.stereotype.Service;
77 public class InterfacesOperationsConverter {
79 public static final String SELF = "SELF";
80 private static final String DERIVED_FROM_STANDARD_INTERFACE = "tosca.interfaces.node.lifecycle.Standard";
81 private static final String DERIVED_FROM_BASE_DEFAULT = "org.openecomp.interfaces.node.lifecycle.";
82 private static final String DEFAULT_HAS_UNDERSCORE = "_default";
83 private static final String DOT = ".";
84 private static final String DEFAULTP = "defaultp";
85 private static final String LOCAL_INTERFACE_TYPE = "Local";
86 private final PropertyConvertor propertyConvertor;
89 public InterfacesOperationsConverter(final PropertyConvertor propertyConvertor) {
90 this.propertyConvertor = propertyConvertor;
93 private static Object getDefaultValue(Map<String, Object> inputValueMap) {
94 Object defaultValue = null;
95 for (Map.Entry<String, Object> operationEntry : inputValueMap.entrySet()) {
96 final Object value = operationEntry.getValue();
97 if (value instanceof Map) {
98 getDefaultValue((Map<String, Object>) value);
100 final String key = operationEntry.getKey();
101 if (key.equals(DEFAULTP)) {
102 defaultValue = inputValueMap.remove(key);
108 //Remove input type and copy default value directly into the proxy node template from the node type
109 private static void handleOperationInputValue(Map<String, Object> operationsMap, String parentKey) {
110 for (Map.Entry<String, Object> operationEntry : operationsMap.entrySet()) {
111 final Object value = operationEntry.getValue();
112 final String key = operationEntry.getKey();
113 if (value instanceof Map) {
114 if (INPUTS.getElementName().equals(parentKey)) {
115 Object defaultValue = getDefaultValue((Map<String, Object>) value);
116 operationsMap.put(key, defaultValue);
118 handleOperationInputValue((Map<String, Object>) value, key);
124 private static String getLastPartOfName(String toscaResourceName) {
125 return toscaResourceName.substring(toscaResourceName.lastIndexOf(DOT) + 1);
128 private static String getInputValue(final OperationInputDefinition input) {
129 if (null != input.getToscaFunction()) {
130 return input.getToscaFunction().getJsonObjectValue().toString();
132 String inputValue = input.getValue() == null ? input.getToscaDefaultValue() : input.getValue();
133 if (inputValue != null && inputValue.contains(ToscaFunctions.GET_OPERATION_OUTPUT.getFunctionName())) {
134 Gson gson = new Gson();
135 Map<String, List<String>> consumptionValue = gson.fromJson(inputValue, Map.class);
136 List<String> mappedOutputValue = consumptionValue.get(ToscaFunctions.GET_OPERATION_OUTPUT.getFunctionName());
137 //Extract the interface name from the interface type
138 String interfaceType = mappedOutputValue.get(1);
139 String interfaceName = interfaceType.substring(interfaceType.lastIndexOf('.') + 1);
140 mappedOutputValue.remove(1);
141 mappedOutputValue.add(1, interfaceName);
142 inputValue = gson.toJson(consumptionValue);
144 String finalInputValue = inputValue;
145 if (inputValue != null &&
146 Arrays.stream(ToscaFunctionType.values()).anyMatch(toscaType -> finalInputValue.contains(toscaType.getName().toUpperCase()))) {
147 inputValue = getInputValuesAsToscaFunction(inputValue);
153 private static String getInputValuesAsToscaFunction(String inputValue) {
154 Gson gson = new Gson();
155 Map<String, Object> mapOfValues = gson.fromJson(inputValue, Map.class);
156 for (Entry<String, Object> entry : mapOfValues.entrySet()) {
157 Object value = entry.getValue();
158 if (value instanceof Map) {
159 Map<String, Object> valueMap = (Map<String, Object>) value;
160 if (valueMap.containsKey("type")) {
161 String type = (String) valueMap.get("type");
162 Optional<ToscaFunctionType> toscaType = ToscaFunctionType.findType(type);
163 if (toscaType.isPresent()) {
164 ObjectMapper mapper = new ObjectMapper();
166 String json = gson.toJson(value);
167 ToscaFunction toscaFunction = mapper.readValue(json, ToscaFunction.class);
168 entry.setValue(toscaFunction.getJsonObjectValue());
169 } catch (JsonProcessingException e) {
170 throw new ByActionStatusComponentException(ActionStatus.GENERAL_ERROR);
176 return String.valueOf(new JSONObject(mapOfValues));
179 private static String getInterfaceType(Component component, String interfaceType) {
180 if (LOCAL_INTERFACE_TYPE.equals(interfaceType)) {
181 return DERIVED_FROM_BASE_DEFAULT + component.getComponentMetadataDefinition().getMetadataDataDefinition().getSystemName();
183 return interfaceType;
186 private static Map<String, Object> getObjectAsMap(final Object obj) {
187 final Map<String, Object> objectAsMap;
188 if (obj instanceof Map) {
189 objectAsMap = (Map<String, Object>) obj;
191 final ObjectMapper objectMapper = new ObjectMapper();
192 final SimpleModule module = new SimpleModule("ToscaPropertyAssignmentSerializer");
193 module.addSerializer(ToscaPropertyAssignment.class, new ToscaPropertyAssignmentJsonSerializer());
194 objectMapper.registerModule(module);
195 if (obj instanceof ToscaInterfaceDefinition) {
196 //Prevent empty field serialization in interface definition
197 objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
199 objectAsMap = objectMapper.convertValue(obj, Map.class);
202 final String defaultEntry = DEFAULT.getElementName();
203 if (objectAsMap.containsKey(defaultEntry)) {
204 objectAsMap.put(DEFAULT_HAS_UNDERSCORE, objectAsMap.remove(defaultEntry));
210 * Creates the interface_types element.
212 * @param component to work on
213 * @return the added element
215 public Map<String, Object> addInterfaceTypeElement(Component component, List<String> allInterfaceTypes) {
216 if (component instanceof Product) {
219 final Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
220 if (MapUtils.isEmpty(interfaces)) {
223 Map<String, Object> toscaInterfaceTypes = new HashMap<>();
224 for (InterfaceDefinition interfaceDefinition : interfaces.values()) {
225 boolean isInterfaceTypeExistInGlobalType = allInterfaceTypes.stream()
226 .anyMatch(type -> type.equalsIgnoreCase(interfaceDefinition.getType()));
227 if (!isInterfaceTypeExistInGlobalType) {
228 ToscaInterfaceNodeType toscaInterfaceType = new ToscaInterfaceNodeType();
229 toscaInterfaceType.setDerived_from(DERIVED_FROM_STANDARD_INTERFACE);
230 final Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
231 Map<String, Object> toscaOperations = new HashMap<>();
232 for (Map.Entry<String, OperationDataDefinition> operationEntry : operations.entrySet()) {
233 toscaOperations.put(operationEntry.getValue().getName(), null);
235 toscaInterfaceType.setOperations(toscaOperations);
236 Map<String, Object> interfacesAsMap = getObjectAsMap(toscaInterfaceType);
237 Map<String, Object> operationsMap = (Map<String, Object>) interfacesAsMap.remove(OPERATIONS.getElementName());
238 interfacesAsMap.putAll(operationsMap);
239 toscaInterfaceTypes.put(getInterfaceType(component, LOCAL_INTERFACE_TYPE), interfacesAsMap);
242 return MapUtils.isNotEmpty(toscaInterfaceTypes) ? toscaInterfaceTypes : null;
245 private boolean isArtifactPresent(final OperationDataDefinition operationDataDefinition) {
246 return operationDataDefinition.getImplementation() != null
247 && StringUtils.isNotEmpty(operationDataDefinition.getImplementation().getArtifactName());
251 * Adds the 'interfaces' element to the node type provided.
253 * @param component to work on
254 * @param nodeType to which the interfaces element will be added
256 public void addInterfaceDefinitionElement(Component component, ToscaNodeType nodeType, Map<String, DataTypeDefinition> dataTypes,
257 boolean isAssociatedComponent) {
258 if (component instanceof Product) {
261 final Map<String, InterfaceDefinition> interfaces = component.getInterfaces();
262 if (MapUtils.isEmpty(interfaces)) {
265 Map<String, Object> toscaInterfaceDefinitions = getInterfacesMap(component, dataTypes, isAssociatedComponent);
266 if (MapUtils.isNotEmpty(toscaInterfaceDefinitions)) {
267 nodeType.setInterfaces(toscaInterfaceDefinitions);
271 private Map<String, Object> getInterfacesMap(Component component, Map<String, DataTypeDefinition> dataTypes, boolean isAssociatedComponent) {
272 return getInterfacesMap(component, null, component.getInterfaces(), dataTypes, isAssociatedComponent);
275 public Map<String, Object> getInterfacesMap(final Component component, final ComponentInstance componentInstance,
276 final Map<String, InterfaceDefinition> interfaces, final Map<String, DataTypeDefinition> dataTypes,
277 final boolean isAssociatedComponent) {
278 if (MapUtils.isEmpty(interfaces)) {
281 final Map<String, Object> toscaInterfaceDefinitions = new HashMap<>();
282 for (InterfaceDefinition interfaceDefinition : interfaces.values()) {
283 handleInterfaceOperations(component, componentInstance, dataTypes, isAssociatedComponent,
284 toscaInterfaceDefinitions, interfaceDefinition);
286 return toscaInterfaceDefinitions;
289 public Map<String, Object> getInterfacesMapFromComponentInstance(final Component component, final ComponentInstance componentInstance,
290 final Map<String, DataTypeDefinition> dataTypes,
291 final boolean isAssociatedComponent) {
292 final Map<String, Object> toscaInterfaceDefinitions = new HashMap<>();
293 final ObjectMapper objectMapper = new ObjectMapper();
294 objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
295 for (final Map.Entry<String, Object> interfaceEntry : componentInstance.getInterfaces().entrySet()) {
296 final InterfaceDefinition interfaceDefinition = objectMapper.convertValue(interfaceEntry.getValue(), InterfaceDefinition.class);
297 handleInterfaceOperations(component, componentInstance, dataTypes, isAssociatedComponent,
298 toscaInterfaceDefinitions, interfaceDefinition);
300 return toscaInterfaceDefinitions;
303 private void handleInterfaceOperations(final Component component, final ComponentInstance componentInstance,
304 final Map<String, DataTypeDefinition> dataTypes, final boolean isAssociatedComponent,
305 final Map<String, Object> toscaInterfaceDefinitions,
306 final InterfaceDefinition interfaceDefinition) {
307 final String interfaceType;
308 if (componentInstance != null && LOCAL_INTERFACE_TYPE.equals(interfaceDefinition.getType())) {
309 interfaceType = DERIVED_FROM_BASE_DEFAULT + componentInstance.getSourceModelName();
311 interfaceType = getInterfaceType(component, interfaceDefinition.getType());
313 final ToscaInterfaceDefinition toscaInterfaceDefinition = new ToscaInterfaceDefinition();
314 if (componentInstance == null) {
315 toscaInterfaceDefinition.setType(interfaceType);
317 final Map<String, OperationDataDefinition> operations = interfaceDefinition.getOperations();
318 final Map<String, Object> toscaOperationMap = new HashMap<>();
319 for (final Entry<String, OperationDataDefinition> operationEntry : operations.entrySet()) {
320 if (operationHasAnImplementation(operationEntry.getValue())) {
321 final ToscaLifecycleOperationDefinition toscaLifecycleOperationDefinition = new ToscaLifecycleOperationDefinition();
322 handleInterfaceOperationImplementation(component, componentInstance, isAssociatedComponent, operationEntry.getValue(),
323 toscaLifecycleOperationDefinition, dataTypes);
324 if (StringUtils.isNotEmpty(operationEntry.getValue().getDescription())) {
325 toscaLifecycleOperationDefinition.setDescription(operationEntry.getValue().getDescription());
327 fillToscaOperationInputs(operationEntry.getValue(), dataTypes, toscaLifecycleOperationDefinition);
328 toscaOperationMap.put(operationEntry.getValue().getName(), toscaLifecycleOperationDefinition);
331 toscaInterfaceDefinition.setOperations(toscaOperationMap);
332 final Map<String, Object> interfaceInputMap = createInterfaceInputMap(interfaceDefinition, dataTypes);
333 if (MapUtils.isNotEmpty(interfaceInputMap)) {
334 toscaInterfaceDefinition.setInputs(interfaceInputMap);
336 final Map<String, Object> interfaceDefinitionAsMap = getObjectAsMap(toscaInterfaceDefinition);
337 if (interfaceDefinitionAsMap.containsKey(INPUTS.getElementName())) {
338 handleDefaults((Map<String, Object>) interfaceDefinitionAsMap.get(INPUTS.getElementName()));
340 final Map<String, Object> operationsMap = (Map<String, Object>) interfaceDefinitionAsMap.remove(OPERATIONS.getElementName());
341 handleOperationInputValue(operationsMap, interfaceType);
342 interfaceDefinitionAsMap.putAll(operationsMap);
343 toscaInterfaceDefinitions.put(getLastPartOfName(interfaceType), interfaceDefinitionAsMap);
346 private boolean operationHasAnImplementation(OperationDataDefinition operation) {
347 return operation.getImplementation() != null && StringUtils.isNotEmpty(operation.getImplementation().getArtifactName()) &&
348 !operation.getImplementation().getArtifactName().equals("''");
351 private void handleInterfaceOperationImplementation(final Component component, final ComponentInstance componentInstance,
352 final boolean isAssociatedComponent,
353 final OperationDataDefinition operationDataDefinition,
354 final ToscaLifecycleOperationDefinition toscaOperation,
355 final Map<String, DataTypeDefinition> dataTypes) {
356 final ArtifactDataDefinition implementation = operationDataDefinition.getImplementation();
357 if (implementation == null) {
361 if (isArtifactPresent(operationDataDefinition)) {
362 final String operationArtifactPath =
363 OperationArtifactUtil.createOperationArtifactPath(component, componentInstance, operationDataDefinition, isAssociatedComponent);
364 if (implementation.getArtifactType() != null) {
365 final ToscaArtifactDefinition toscaArtifactDefinition = new ToscaArtifactDefinition();
366 toscaArtifactDefinition.setFile(operationArtifactPath);
367 final String artifactVersion = implementation.getArtifactVersion();
368 toscaArtifactDefinition.setArtifact_version(!artifactVersion.equals(NumberUtils.INTEGER_ZERO.toString()) ? artifactVersion : null);
369 toscaArtifactDefinition.setType(implementation.getArtifactType());
370 final Map<String, ToscaPropertyAssignment> propertiesMap = handleImplementationProperties(operationDataDefinition, dataTypes);
371 if (MapUtils.isNotEmpty(propertiesMap)) {
372 toscaArtifactDefinition.setProperties(propertiesMap);
374 final ToscaInterfaceOperationImplementation toscaInterfaceOperationImplementation = new ToscaInterfaceOperationImplementation();
375 toscaInterfaceOperationImplementation.setPrimary(toscaArtifactDefinition);
376 toscaOperation.setImplementation(toscaInterfaceOperationImplementation);
378 toscaOperation.setImplementation(
379 StringUtils.isBlank(operationArtifactPath) || "null".equals(operationArtifactPath) ? null : operationArtifactPath);
384 private Map<String, ToscaPropertyAssignment> handleImplementationProperties(final OperationDataDefinition operationDataDefinition,
385 final Map<String, DataTypeDefinition> dataTypes) {
386 if (operationDataDefinition.getImplementation() == null) {
387 return new HashMap<>();
390 final List<PropertyDataDefinition> properties = operationDataDefinition.getImplementation().getProperties();
391 if (CollectionUtils.isEmpty(properties)) {
392 return new HashMap<>();
395 final Map<String, ToscaPropertyAssignment> propertiesMap = new HashMap<>();
397 .filter(propertyDataDefinition -> StringUtils.isNotEmpty(propertyDataDefinition.getValue()))
398 .forEach(propertyDataDefinition -> {
399 final String propertyValue =
400 propertyDataDefinition.getValue() != null ? propertyDataDefinition.getValue() : propertyDataDefinition.getDefaultValue();
401 final ToscaPropertyAssignment toscaPropertyAssignment = new ToscaPropertyAssignment();
402 toscaPropertyAssignment.setValue(propertyConvertor.convertToToscaObject(propertyDataDefinition, propertyValue, dataTypes, false));
403 propertiesMap.put(propertyDataDefinition.getName(), toscaPropertyAssignment);
407 return propertiesMap;
410 public void removeInterfacesWithoutOperations(final Map<String, Object> interfaceMap) {
411 if (MapUtils.isEmpty(interfaceMap)) {
414 final Set<String> emptyInterfaces = interfaceMap.entrySet().stream().filter(entry -> {
415 final Object value = entry.getValue();
416 if (value instanceof ToscaInterfaceDefinition) {
417 final ToscaInterfaceDefinition interfaceDefinition = (ToscaInterfaceDefinition) value;
418 return MapUtils.isEmpty(interfaceDefinition.getOperations());
419 } else if (value instanceof Map) {
420 final Map<String, Object> interfaceDefMap = (Map<String, Object>) value;
421 return MapUtils.isEmpty(interfaceDefMap);
424 }).map(Entry::getKey).collect(Collectors.toSet());
425 emptyInterfaces.forEach(interfaceMap::remove);
428 private Map<String, Object> createInterfaceInputMap(final InterfaceDefinition interfaceDefinition,
429 final Map<String, DataTypeDefinition> allDataTypeMap) {
430 final Map<String, InputDataDefinition> inputMap = interfaceDefinition.getInputs();
431 if (MapUtils.isEmpty(inputMap)) {
432 return Collections.emptyMap();
434 final Map<String, Object> toscaInterfaceInputMap = new HashMap<>();
435 for (final Entry<String, InputDataDefinition> inputEntry : inputMap.entrySet()) {
436 final InputDataDefinition inputDataDefinition = inputEntry.getValue();
437 final ToscaProperty toscaProperty = propertyConvertor
438 .convertProperty(allDataTypeMap, new PropertyDefinition(inputDataDefinition), PropertyType.INPUT);
439 toscaInterfaceInputMap.put(inputEntry.getKey(), new ToscaInput(toscaProperty));
441 return toscaInterfaceInputMap;
445 * workaround for : currently "defaultp" is not being converted to "default" by the relevant code in
446 * ToscaExportHandler so, any string Map key named "defaultp" will have its named changed to "default"
447 * @param operationsMap the map to update
449 private void handleDefaults(Map<String, Object> operationsMap) {
450 for (Map.Entry<String, Object> operationEntry : operationsMap.entrySet()) {
451 final Object value = operationEntry.getValue();
452 if (value instanceof Map) {
453 handleDefaults((Map<String, Object>) value);
455 final String key = operationEntry.getKey();
456 if (key.equals(DEFAULTP)) {
457 Object removed = operationsMap.remove(key);
458 operationsMap.put(ToscaTagNamesEnum.DEFAULT.getElementName(), removed);
463 private void fillToscaOperationInputs(OperationDataDefinition operation, Map<String, DataTypeDefinition> dataTypes,
464 ToscaLifecycleOperationDefinition toscaOperation) {
465 if (Objects.isNull(operation.getInputs()) || operation.getInputs().isEmpty()) {
466 toscaOperation.setInputs(null);
469 Map<String, ToscaProperty> toscaInputs = new HashMap<>();
470 for (OperationInputDefinition input : operation.getInputs().getListToscaDataDefinition()) {
471 ToscaProperty toscaInput = new ToscaProperty();
472 toscaInput.setDescription(input.getDescription());
473 toscaInput.setType(input.getType());
474 toscaInput.setRequired(input.isRequired());
475 toscaInput.setDefaultp(propertyConvertor.convertToToscaObject(input, getInputValue(input), dataTypes, false));
476 toscaInputs.put(input.getName(), toscaInput);
478 toscaOperation.setInputs(toscaInputs);