Fix empty interface operation exception
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / InterfaceDefinitionHandler.java
1 /*
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2020 Nordix Foundation
4  *  ================================================================================
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *        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.
15  *
16  *  SPDX-License-Identifier: Apache-2.0
17  *  ============LICENSE_END=========================================================
18  */
19 package org.openecomp.sdc.be.components.impl;
20
21 import static org.openecomp.sdc.be.components.impl.ImportUtils.Constants.QUOTE;
22 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.DEFAULT;
23 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.DESCRIPTION;
24 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.IMPLEMENTATION;
25 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.INPUTS;
26 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.NOTIFICATIONS;
27 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.OPERATIONS;
28 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.REQUIRED;
29 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.STATUS;
30 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.TYPE;
31
32 import com.google.gson.Gson;
33 import fj.data.Either;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.LinkedHashMap;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.Map.Entry;
41 import java.util.Optional;
42 import java.util.UUID;
43 import java.util.stream.Collectors;
44 import org.apache.commons.collections.MapUtils;
45 import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
46 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
47 import org.openecomp.sdc.be.dao.api.ActionStatus;
48 import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
49 import org.openecomp.sdc.be.datatypes.elements.InputDataDefinition;
50 import org.openecomp.sdc.be.datatypes.elements.ListDataDefinition;
51 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
52 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
53 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
54 import org.openecomp.sdc.be.model.InputDefinition;
55 import org.openecomp.sdc.be.model.InterfaceDefinition;
56 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
57 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
58 import org.openecomp.sdc.be.tosca.utils.OperationArtifactUtil;
59 import org.openecomp.sdc.exception.ResponseFormat;
60 import org.slf4j.Logger;
61 import org.slf4j.LoggerFactory;
62 import org.springframework.stereotype.Component;
63
64 /**
65  * Handles interface definition TOSCA conversions
66  */
67 @Component("interfaceDefinitionHandler")
68 public class InterfaceDefinitionHandler {
69
70     private static final Logger LOGGER = LoggerFactory.getLogger(InterfaceDefinitionHandler.class);
71     private static final String WITH_ATTRIBUTE = "with attribute '{}': '{}'";
72     private final InterfaceOperationBusinessLogic interfaceOperationBusinessLogic;
73
74     public InterfaceDefinitionHandler(final InterfaceOperationBusinessLogic interfaceOperationBusinessLogic) {
75         this.interfaceOperationBusinessLogic = interfaceOperationBusinessLogic;
76     }
77
78     /**
79      * Creates an interface definition based on a TOSCA map representing an interface definition.
80      *
81      * @param interfaceDefinitionToscaMap the TOSCA interface definition structure
82      * @return an interface definition representation
83      */
84     public InterfaceDefinition create(final Map<String, Object> interfaceDefinitionToscaMap, final String model) {
85         final InterfaceDefinition interfaceDefinition = new InterfaceDefinition();
86         interfaceDefinition.setModel(model);
87         if (interfaceDefinitionToscaMap.containsKey(TYPE.getElementName())) {
88             final Object typeObj = interfaceDefinitionToscaMap.get(TYPE.getElementName());
89             if (!(typeObj instanceof String)) {
90                 throw new ByActionStatusComponentException(ActionStatus.INVALID_YAML);
91             }
92             final String type = (String) typeObj;
93             interfaceDefinition.setType(type);
94             interfaceDefinition.setUniqueId(type);
95         }
96         final Map<String, InputDefinition> inputDefinitionMap = handleInputs(interfaceDefinitionToscaMap);
97         if (!inputDefinitionMap.isEmpty()) {
98             final Map<String, InputDataDefinition> collect = inputDefinitionMap.entrySet().stream()
99                 .collect(Collectors.toMap(Entry::getKey, value -> new InputDataDefinition(value.getValue())));
100             interfaceDefinition.setInputs(collect);
101         }
102         final Map<String, OperationDataDefinition> operationMap;
103         if (interfaceDefinitionToscaMap.containsKey(OPERATIONS.getElementName()) || interfaceDefinitionToscaMap
104             .containsKey(NOTIFICATIONS.getElementName())) {
105             operationMap = handleOperations(interfaceDefinitionToscaMap);
106             //TODO handle notifications
107         } else {
108             operationMap = handleLegacyOperations(interfaceDefinitionToscaMap);
109         }
110         if (!operationMap.isEmpty()) {
111             validateOperations(interfaceDefinition.getType(), operationMap, model);
112             interfaceDefinition.setOperations(operationMap);
113         }
114         return interfaceDefinition;
115     }
116
117     private void validateOperations(final String interfaceType, final Map<String, OperationDataDefinition> operationMap, final String model) {
118         if (MapUtils.isEmpty(operationMap)) {
119             return;
120         }
121         Either<Map<String, InterfaceDefinition>, ResponseFormat> interfaceDefinitionMapEither = interfaceOperationBusinessLogic
122             .getAllInterfaceLifecycleTypes(model);
123         if (interfaceDefinitionMapEither.isRight() || MapUtils.isEmpty(interfaceDefinitionMapEither.left().value())) {
124             throw new ByActionStatusComponentException(ActionStatus.INTERFACE_UNKNOWN, interfaceType);
125         }
126         final Map<String, InterfaceDefinition> interfaceDefinitionMap = interfaceDefinitionMapEither.left().value();
127         final Optional<InterfaceDefinition> interfaceDefinitionOptional = interfaceDefinitionMap.entrySet().stream()
128             .filter(interfaceDefinitionEntry -> interfaceDefinitionEntry.getKey().equalsIgnoreCase(UniqueIdBuilder.buildInterfaceTypeUid(model, interfaceType))).map(Entry::getValue).findFirst();
129         if (interfaceDefinitionOptional.isEmpty()) {
130             throw new ByActionStatusComponentException(ActionStatus.INTERFACE_UNKNOWN, interfaceType);
131         }
132         final InterfaceDefinition interfaceDefinition = interfaceDefinitionOptional.get();
133         operationMap.keySet().forEach(operation1 -> {
134             if (!interfaceDefinition.hasOperation(operation1)) {
135                 throw new ByActionStatusComponentException(ActionStatus.INTERFACE_OPERATION_NOT_DEFINED, operation1, interfaceType);
136             }
137         });
138     }
139
140     private Map<String, OperationDataDefinition> handleOperations(final Map<String, Object> interfaceDefinitionToscaMap) {
141         if (!interfaceDefinitionToscaMap.containsKey(OPERATIONS.getElementName())) {
142             return Collections.emptyMap();
143         }
144         final Map<String, Object> operationMap = (Map<String, Object>) interfaceDefinitionToscaMap.get(OPERATIONS.getElementName());
145         return operationMap.entrySet().stream()
146             .map(interfaceEntry -> createOperation(interfaceEntry.getKey(), (Map<String, Object>) interfaceEntry.getValue()))
147             .collect(Collectors.toMap(OperationDataDefinition::getName, operationDataDefinition -> operationDataDefinition));
148     }
149
150     private Map<String, OperationDataDefinition> handleLegacyOperations(final Map<String, Object> interfaceDefinitionToscaMap) {
151         final List<String> notALegacyOperationEntry = Arrays
152             .asList(OPERATIONS.getElementName(), TYPE.getElementName(), INPUTS.getElementName(), NOTIFICATIONS.getElementName());
153         return interfaceDefinitionToscaMap.entrySet().stream().filter(interfaceEntry -> !notALegacyOperationEntry.contains(interfaceEntry.getKey()))
154             .map(interfaceEntry -> createOperation(interfaceEntry.getKey(), (Map<String, Object>) interfaceEntry.getValue()))
155             .collect(Collectors.toMap(OperationDataDefinition::getName, operationDataDefinition -> operationDataDefinition));
156     }
157
158     private OperationDataDefinition createOperation(final String operationName, final Map<String, Object> operationDefinitionMap) {
159         final OperationDataDefinition operation = new OperationDataDefinition();
160         operation.setUniqueId(UUID.randomUUID().toString());
161         operation.setName(operationName);
162         
163         if (MapUtils.isEmpty(operationDefinitionMap)) {
164             return operation;
165         }
166         Object operationDescription = operationDefinitionMap.get(
167                 DESCRIPTION.getElementName()
168         );
169         if (null != operationDescription) {
170             operation.setDescription(operationDescription.toString());
171         }
172         operation.setImplementation(handleOperationImplementation(operationDefinitionMap).orElse(new ArtifactDataDefinition()));
173         if (operationDefinitionMap.containsKey(INPUTS.getElementName())) {
174             final Map<String, Object> interfaceInputs = (Map<String, Object>) operationDefinitionMap.get(INPUTS.getElementName());
175             operation.setInputs(handleInterfaceOperationInputs(interfaceInputs));
176         }
177         return operation;
178     }
179
180     private ListDataDefinition<OperationInputDefinition> handleInterfaceOperationInputs(final Map<String, Object> interfaceInputs) {
181         final ListDataDefinition<OperationInputDefinition> inputs = new ListDataDefinition<>();
182         for (final Entry<String, Object> interfaceInput : interfaceInputs.entrySet()) {
183             final OperationInputDefinition operationInput = new OperationInputDefinition();
184             operationInput.setUniqueId(UUID.randomUUID().toString());
185             operationInput.setInputId(operationInput.getUniqueId());
186             operationInput.setName(interfaceInput.getKey());
187             handleInputToscaDefinition(interfaceInput.getKey(), interfaceInput.getValue(), operationInput);
188             inputs.add(operationInput);
189         }
190         return inputs;
191     }
192
193     private void handleInputToscaDefinition(final String inputName, final Object value, final OperationInputDefinition operationInput) {
194         if (value instanceof Map) {
195             final LinkedHashMap<String, Object> inputPropertyValue = (LinkedHashMap<String, Object>) value;
196             LOGGER.debug("Creating interface operation input '{}'", inputName);
197             if (inputPropertyValue.get(TYPE.getElementName()) != null) {
198                 final String type = inputPropertyValue.get(TYPE.getElementName()).toString();
199                 LOGGER.debug(WITH_ATTRIBUTE, TYPE.getElementName(), type);
200                 operationInput.setType(type);
201             }
202             if (inputPropertyValue.get(DESCRIPTION.getElementName()) != null) {
203                 final String description = inputPropertyValue.get(DESCRIPTION.getElementName()).toString();
204                 LOGGER.debug(WITH_ATTRIBUTE, DESCRIPTION.getElementName(), description);
205                 operationInput.setDescription(description);
206             }
207             if (inputPropertyValue.get(REQUIRED.getElementName()) != null) {
208                 final boolean required = Boolean.parseBoolean(inputPropertyValue.get(REQUIRED.getElementName()).toString());
209                 LOGGER.debug(WITH_ATTRIBUTE, REQUIRED.getElementName(), required);
210                 operationInput.setRequired(required);
211             }
212             if (inputPropertyValue.get(DEFAULT.getElementName()) != null) {
213                 final Gson gson = new Gson();
214                 final String json = gson.toJson(inputPropertyValue.get(DEFAULT.getElementName()));
215                 LOGGER.debug(WITH_ATTRIBUTE, DEFAULT.getElementName(), json);
216                 operationInput.setToscaDefaultValue(json);
217             }
218             if (inputPropertyValue.get(STATUS.getElementName()) != null) {
219                 final String status = inputPropertyValue.get(STATUS.getElementName()).toString();
220                 LOGGER.debug(WITH_ATTRIBUTE, STATUS.getElementName(), status);
221                 operationInput.setStatus(status);
222             }
223             return;
224         }
225         if (value instanceof String) {
226             final String stringValue = (String) value;
227             operationInput.setDefaultValue(stringValue);
228             operationInput.setToscaDefaultValue(stringValue);
229             operationInput.setValue(stringValue);
230         }
231     }
232
233     @SuppressWarnings("unchecked")
234     private Optional<ArtifactDataDefinition> handleOperationImplementation(final Map<String, Object> operationDefinitionMap) {
235         if (!operationDefinitionMap.containsKey(IMPLEMENTATION.getElementName())) {
236             return Optional.empty();
237         }
238         final ArtifactDataDefinition artifactDataDefinition = new ArtifactDataDefinition();
239         if (operationDefinitionMap.get(IMPLEMENTATION.getElementName()) instanceof Map && 
240                 ((Map)operationDefinitionMap.get(IMPLEMENTATION.getElementName())).containsKey("primary")) {
241             Map<String, Object> implDetails = (Map) ((Map)operationDefinitionMap.get(IMPLEMENTATION.getElementName())).get("primary");
242             
243             if (implDetails.get("file") != null) {
244                 final String file = implDetails.get("file").toString();
245                 artifactDataDefinition.setArtifactName(generateArtifactName(file));
246             }
247             if (implDetails.get("type") != null) {
248                 artifactDataDefinition.setArtifactType(implDetails.get("type").toString());
249             }
250             if (implDetails.get("artifact_version") != null) {
251                 artifactDataDefinition.setArtifactVersion(implDetails.get("artifact_version").toString());
252             }
253             
254             if(implDetails.get("properties") instanceof Map) {
255                 List<PropertyDataDefinition> operationProperties = artifactDataDefinition.getProperties() == null ? new ArrayList<>() : artifactDataDefinition.getProperties();
256                 Map<String, Object> properties = (Map<String, Object>) implDetails.get("properties");
257                 properties.forEach((k,v) -> {
258                     ToscaPropertyType type = getTypeFromObject(v);
259                     if (type != null) {
260                         PropertyDataDefinition propertyDef = new PropertyDataDefinition();
261                         propertyDef.setName(k);
262                         propertyDef.setType(type.getType());
263                         propertyDef.setValue(v.toString());
264                         artifactDataDefinition.addProperty(propertyDef);
265                     }
266                 });
267             }
268         }
269         if (operationDefinitionMap.get(IMPLEMENTATION.getElementName()) instanceof String) {
270             final String implementation = (String) operationDefinitionMap.get(IMPLEMENTATION.getElementName());
271             artifactDataDefinition.setArtifactName(generateArtifactName(implementation));
272         }
273         return Optional.of(artifactDataDefinition);
274     }
275     
276     private String generateArtifactName(final String name) {
277         if (OperationArtifactUtil.artifactNameIsALiteralValue(name)) {
278            return name;
279         } else {
280             return QUOTE + name + QUOTE;
281         }
282     }
283     
284     private ToscaPropertyType getTypeFromObject(final Object value) {
285         if (value instanceof String) {
286             return ToscaPropertyType.STRING;
287         }
288         if (value instanceof Integer) {
289             return ToscaPropertyType.INTEGER;
290         }
291         if (value instanceof Boolean) {
292             return ToscaPropertyType.BOOLEAN;
293         }
294         if (value instanceof Float || value instanceof Double) {
295             return ToscaPropertyType.FLOAT;
296         }
297         return null;
298     }
299     
300
301     private Map<String, InputDefinition> handleInputs(final Map<String, Object> interfaceDefinitionToscaMap) {
302         if (!interfaceDefinitionToscaMap.containsKey(INPUTS.getElementName())) {
303             return Collections.emptyMap();
304         }
305         final Either<Map<String, InputDefinition>, ResultStatusEnum> inputMapEither = ImportUtils.getInputs(interfaceDefinitionToscaMap);
306         if (inputMapEither.isRight()) {
307             return Collections.emptyMap();
308         }
309         return inputMapEither.left().value();
310     }
311 }