Add support for updating interface operations
[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
20 package org.openecomp.sdc.be.components.impl;
21
22 import static org.openecomp.sdc.be.components.impl.ImportUtils.Constants.QUOTE;
23 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.DEFAULT;
24 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.DESCRIPTION;
25 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.IMPLEMENTATION;
26 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.INPUTS;
27 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.NOTIFICATIONS;
28 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.OPERATIONS;
29 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.REQUIRED;
30 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.STATUS;
31 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.TYPE;
32
33 import fj.data.Either;
34 import java.util.Arrays;
35 import java.util.Collections;
36 import java.util.LinkedHashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.Map.Entry;
40 import java.util.Optional;
41 import java.util.UUID;
42 import java.util.stream.Collectors;
43 import org.apache.commons.collections.MapUtils;
44 import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
45 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
46 import org.openecomp.sdc.be.dao.api.ActionStatus;
47 import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
48 import org.openecomp.sdc.be.datatypes.elements.InputDataDefinition;
49 import org.openecomp.sdc.be.datatypes.elements.ListDataDefinition;
50 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
51 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
52 import org.openecomp.sdc.be.model.InputDefinition;
53 import org.openecomp.sdc.be.model.InterfaceDefinition;
54 import org.openecomp.sdc.be.tosca.utils.OperationArtifactUtil;
55 import org.openecomp.sdc.exception.ResponseFormat;
56 import org.slf4j.Logger;
57 import org.slf4j.LoggerFactory;
58 import org.springframework.stereotype.Component;
59
60 /**
61  * Handles interface definition TOSCA conversions
62  */
63 @Component("interfaceDefinitionHandler")
64 public class InterfaceDefinitionHandler {
65
66     private static final Logger LOGGER = LoggerFactory.getLogger(InterfaceDefinitionHandler.class);
67
68     private final InterfaceOperationBusinessLogic interfaceOperationBusinessLogic;
69
70     public InterfaceDefinitionHandler(final InterfaceOperationBusinessLogic interfaceOperationBusinessLogic) {
71         this.interfaceOperationBusinessLogic = interfaceOperationBusinessLogic;
72     }
73
74     /**
75      * Creates an interface definition based on a TOSCA map representing an interface definition.
76      *
77      * @param interfaceDefinitionToscaMap the TOSCA interface definition structure
78      * @return an interface definition representation
79      */
80     public InterfaceDefinition create(final Map<String, Object> interfaceDefinitionToscaMap) {
81
82         final InterfaceDefinition interfaceDefinition = new InterfaceDefinition();
83
84         if (interfaceDefinitionToscaMap.containsKey(TYPE.getElementName())) {
85             final Object typeObj = interfaceDefinitionToscaMap.get(TYPE.getElementName());
86             if (!(typeObj instanceof String)) {
87                 throw new ByActionStatusComponentException(ActionStatus.INVALID_YAML);
88             }
89             final String type = (String) typeObj;
90             interfaceDefinition.setType(type);
91             interfaceDefinition.setUniqueId(type.toLowerCase());
92         }
93
94         final Map<String, InputDefinition> inputDefinitionMap = handleInputs(interfaceDefinitionToscaMap);
95         if (!inputDefinitionMap.isEmpty()) {
96             final Map<String, InputDataDefinition> collect = inputDefinitionMap.entrySet().stream().
97                 collect(Collectors.toMap(Entry::getKey, value -> new InputDataDefinition(value.getValue())));
98             interfaceDefinition.setInputs(collect);
99         }
100         final Map<String, OperationDataDefinition> operationMap;
101         if (interfaceDefinitionToscaMap.containsKey(OPERATIONS.getElementName()) || interfaceDefinitionToscaMap
102             .containsKey(NOTIFICATIONS.getElementName())) {
103             operationMap = handleOperations(interfaceDefinitionToscaMap);
104             //TODO handle notifications
105         } else {
106             operationMap = handleLegacyOperations(interfaceDefinitionToscaMap);
107         }
108         if (!operationMap.isEmpty()) {
109             validateOperations(interfaceDefinition.getType(), operationMap);
110             interfaceDefinition.setOperations(operationMap);
111         }
112
113         return interfaceDefinition;
114     }
115
116     private void validateOperations(final String interfaceType,
117                                     final Map<String, OperationDataDefinition> operationMap) {
118         if (MapUtils.isEmpty(operationMap)) {
119             return;
120         }
121         Either<Map<String, InterfaceDefinition>, ResponseFormat> interfaceDefinitionMapEither =
122             interfaceOperationBusinessLogic.getAllInterfaceLifecycleTypes();
123         if (interfaceDefinitionMapEither.isRight() || MapUtils.isEmpty(interfaceDefinitionMapEither.left().value())) {
124             throw new ByActionStatusComponentException(ActionStatus.INTERFACE_UNKNOWN, interfaceType);
125         }
126
127         final Map<String, InterfaceDefinition> interfaceDefinitionMap = interfaceDefinitionMapEither.left().value();
128         final Optional<InterfaceDefinition> interfaceDefinitionOptional = interfaceDefinitionMap.entrySet().stream()
129             .filter(interfaceDefinitionEntry -> interfaceDefinitionEntry.getKey().equalsIgnoreCase(interfaceType))
130             .map(Entry::getValue).findFirst();
131         if (interfaceDefinitionOptional.isEmpty()) {
132             throw new ByActionStatusComponentException(ActionStatus.INTERFACE_UNKNOWN, interfaceType);
133         }
134         final InterfaceDefinition interfaceDefinition = interfaceDefinitionOptional.get();
135         operationMap.keySet().forEach(operation1 -> {
136             if (!interfaceDefinition.hasOperation(operation1)) {
137                 throw new ByActionStatusComponentException(ActionStatus.INTERFACE_OPERATION_NOT_DEFINED,
138                     operation1, interfaceType);
139             }
140         });
141     }
142
143     private Map<String, OperationDataDefinition> handleOperations(
144         final Map<String, Object> interfaceDefinitionToscaMap) {
145         if (!interfaceDefinitionToscaMap.containsKey(OPERATIONS.getElementName())) {
146             return Collections.emptyMap();
147         }
148         final Map<String, Object> operationMap =
149             (Map<String, Object>) interfaceDefinitionToscaMap.get(OPERATIONS.getElementName());
150         return operationMap.entrySet().stream()
151             .map(interfaceEntry -> createOperation(interfaceEntry.getKey(),
152                 (Map<String, Object>) interfaceEntry.getValue()))
153             .collect(
154                 Collectors.toMap(OperationDataDefinition::getName, operationDataDefinition -> operationDataDefinition));
155     }
156
157     private Map<String, OperationDataDefinition> handleLegacyOperations(
158         final Map<String, Object> interfaceDefinitionToscaMap) {
159         final List<String> notALegacyOperationEntry = Arrays
160             .asList(OPERATIONS.getElementName(),
161                 TYPE.getElementName(),
162                 INPUTS.getElementName(),
163                 NOTIFICATIONS.getElementName());
164         return interfaceDefinitionToscaMap.entrySet().stream()
165             .filter(interfaceEntry -> !notALegacyOperationEntry.contains(interfaceEntry.getKey()))
166             .map(interfaceEntry -> createOperation(interfaceEntry.getKey(),
167                 (Map<String, Object>) interfaceEntry.getValue()))
168             .collect(
169                 Collectors.toMap(OperationDataDefinition::getName, operationDataDefinition -> operationDataDefinition));
170     }
171
172
173     private OperationDataDefinition createOperation(final String operationName,
174                                                     final Map<String, Object> operationDefinitionMap) {
175         final OperationDataDefinition operation = new OperationDataDefinition();
176         operation.setUniqueId(UUID.randomUUID().toString());
177         operation.setName(operationName);
178
179         operation.setImplementation(
180             handleOperationImplementation(operationDefinitionMap)
181                 .orElse(new ArtifactDataDefinition())
182         );
183         if (operationDefinitionMap.containsKey(INPUTS.getElementName())) {
184             final Map<String, Object> interfaceInputs =
185                 (Map<String, Object>) operationDefinitionMap.get(INPUTS.getElementName());
186             operation.setInputs(handleInterfaceOperationInputs(interfaceInputs));
187         }
188
189         return operation;
190     }
191
192     private ListDataDefinition<OperationInputDefinition> handleInterfaceOperationInputs(
193         final Map<String, Object> interfaceInputs) {
194         final ListDataDefinition<OperationInputDefinition> inputs = new ListDataDefinition<>();
195         for (final Entry<String, Object> interfaceInput : interfaceInputs.entrySet()) {
196             final OperationInputDefinition operationInput = new OperationInputDefinition();
197             operationInput.setUniqueId(UUID.randomUUID().toString());
198             operationInput.setInputId(operationInput.getUniqueId());
199             operationInput.setName(interfaceInput.getKey());
200             if (interfaceInput.getValue() instanceof Map) {
201                 final LinkedHashMap<String, Object> inputPropertyValue =
202                     (LinkedHashMap<String, Object>) interfaceInput.getValue();
203                 LOGGER.info("createModuleInterface: i interfaceInput.getKey() {}, {} , {}  ",
204                     interfaceInput.getKey(), inputPropertyValue.keySet(), inputPropertyValue.values());
205                 if (inputPropertyValue.get(TYPE.getElementName()) != null) {
206                     operationInput.setType(inputPropertyValue.get(TYPE.getElementName()).toString());
207                 }
208                 if (inputPropertyValue.get(DESCRIPTION.getElementName()) != null) {
209                     operationInput.setDescription(inputPropertyValue.get(DESCRIPTION.getElementName()).toString());
210                 }
211                 if (inputPropertyValue.get(REQUIRED.getElementName()) != null) {
212                     operationInput.setRequired(
213                         Boolean.getBoolean(inputPropertyValue.get(REQUIRED.getElementName()).toString()));
214                 }
215                 if (inputPropertyValue.get(DEFAULT.getElementName()) != null) {
216                     operationInput.setToscaDefaultValue(inputPropertyValue.get(DEFAULT.getElementName()).toString());
217                 }
218                 if (inputPropertyValue.get(STATUS.getElementName()) != null) {
219                     operationInput.setStatus(inputPropertyValue.get(STATUS.getElementName()).toString());
220                 }
221
222             } else if (interfaceInput.getValue() instanceof String) {
223                 final String value = (String) interfaceInput.getValue();
224                 operationInput.setDefaultValue(value);
225                 operationInput.setToscaDefaultValue(value);
226                 operationInput.setValue(value);
227             }
228             inputs.add(operationInput);
229         }
230         return inputs;
231     }
232
233     private Optional<ArtifactDataDefinition> handleOperationImplementation(
234         final Map<String, Object> operationDefinitionMap) {
235         if (!operationDefinitionMap.containsKey(IMPLEMENTATION.getElementName())) {
236             return Optional.empty();
237         }
238         final ArtifactDataDefinition artifactDataDefinition = new ArtifactDataDefinition();
239         final String artifactName = (String) operationDefinitionMap.get(IMPLEMENTATION.getElementName());
240         if (OperationArtifactUtil.artifactNameIsALiteralValue(artifactName)) {
241             artifactDataDefinition.setArtifactName(artifactName);
242         } else {
243             artifactDataDefinition.setArtifactName(QUOTE + artifactName + QUOTE);
244         }
245         return Optional.of(artifactDataDefinition);
246     }
247
248     private Map<String, InputDefinition> handleInputs(final Map<String, Object> interfaceDefinitionToscaMap) {
249         if (!interfaceDefinitionToscaMap.containsKey(INPUTS.getElementName())) {
250             return Collections.emptyMap();
251         }
252
253         final Either<Map<String, InputDefinition>, ResultStatusEnum> inputMapEither =
254             ImportUtils.getInputs(interfaceDefinitionToscaMap);
255         if (inputMapEither.isRight()) {
256             return Collections.emptyMap();
257         }
258
259         return inputMapEither.left().value();
260     }
261
262 }