Backend support for operation milestones with activities
[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.ACTIVITIES;
24 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.DEFAULT;
25 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.DESCRIPTION;
26 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.IMPLEMENTATION;
27 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.INPUTS;
28 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.MILESTONES;
29 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.NOTIFICATIONS;
30 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.OPERATIONS;
31 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.REQUIRED;
32 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.STATUS;
33 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.TYPE;
34 import static org.openecomp.sdc.be.utils.TypeUtils.ToscaTagNamesEnum.WORKFLOW;
35
36 import com.google.gson.Gson;
37 import fj.data.Either;
38 import java.util.ArrayList;
39 import java.util.Arrays;
40 import java.util.Collections;
41 import java.util.HashMap;
42 import java.util.LinkedHashMap;
43 import java.util.List;
44 import java.util.Map;
45 import java.util.Map.Entry;
46 import java.util.Optional;
47 import java.util.UUID;
48 import java.util.stream.Collectors;
49 import org.apache.commons.collections.MapUtils;
50 import org.openecomp.sdc.be.components.impl.ImportUtils.ResultStatusEnum;
51 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
52 import org.openecomp.sdc.be.dao.api.ActionStatus;
53 import org.openecomp.sdc.be.datatypes.elements.ActivityDataDefinition;
54 import org.openecomp.sdc.be.datatypes.elements.ArtifactDataDefinition;
55 import org.openecomp.sdc.be.datatypes.elements.InputDataDefinition;
56 import org.openecomp.sdc.be.datatypes.elements.ListDataDefinition;
57 import org.openecomp.sdc.be.datatypes.elements.MilestoneDataDefinition;
58 import org.openecomp.sdc.be.datatypes.elements.OperationDataDefinition;
59 import org.openecomp.sdc.be.datatypes.elements.OperationInputDefinition;
60 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
61 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
62 import org.openecomp.sdc.be.model.InputDefinition;
63 import org.openecomp.sdc.be.model.InterfaceDefinition;
64 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
65 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
66 import org.openecomp.sdc.be.tosca.utils.OperationArtifactUtil;
67 import org.openecomp.sdc.exception.ResponseFormat;
68 import org.slf4j.Logger;
69 import org.slf4j.LoggerFactory;
70 import org.springframework.stereotype.Component;
71
72 /**
73  * Handles interface definition TOSCA conversions
74  */
75 @Component("interfaceDefinitionHandler")
76 public class InterfaceDefinitionHandler {
77
78     private static final Logger LOGGER = LoggerFactory.getLogger(InterfaceDefinitionHandler.class);
79     private static final String WITH_ATTRIBUTE = "with attribute '{}': '{}'";
80     private final InterfaceOperationBusinessLogic interfaceOperationBusinessLogic;
81
82     public InterfaceDefinitionHandler(final InterfaceOperationBusinessLogic interfaceOperationBusinessLogic) {
83         this.interfaceOperationBusinessLogic = interfaceOperationBusinessLogic;
84     }
85
86     /**
87      * Creates an interface definition based on a TOSCA map representing an interface definition.
88      *
89      * @param interfaceDefinitionToscaMap the TOSCA interface definition structure
90      * @return an interface definition representation
91      */
92     public InterfaceDefinition create(final Map<String, Object> interfaceDefinitionToscaMap, final String model) {
93         final InterfaceDefinition interfaceDefinition = new InterfaceDefinition();
94         interfaceDefinition.setModel(model);
95         if (interfaceDefinitionToscaMap.containsKey(TYPE.getElementName())) {
96             final Object typeObj = interfaceDefinitionToscaMap.get(TYPE.getElementName());
97             if (!(typeObj instanceof String)) {
98                 throw new ByActionStatusComponentException(ActionStatus.INVALID_YAML);
99             }
100             final String type = (String) typeObj;
101             interfaceDefinition.setType(type);
102             interfaceDefinition.setUniqueId(type);
103         }
104         final Map<String, InputDefinition> inputDefinitionMap = handleInputs(interfaceDefinitionToscaMap);
105         if (!inputDefinitionMap.isEmpty()) {
106             final Map<String, InputDataDefinition> collect = inputDefinitionMap.entrySet().stream()
107                 .collect(Collectors.toMap(Entry::getKey, value -> new InputDataDefinition(value.getValue())));
108             interfaceDefinition.setInputs(collect);
109         }
110         final Map<String, OperationDataDefinition> operationMap;
111         if (interfaceDefinitionToscaMap.containsKey(OPERATIONS.getElementName()) || interfaceDefinitionToscaMap
112             .containsKey(NOTIFICATIONS.getElementName())) {
113             operationMap = handleOperations(interfaceDefinitionToscaMap);
114             //TODO handle notifications
115         } else {
116             operationMap = handleLegacyOperations(interfaceDefinitionToscaMap);
117         }
118         if (!operationMap.isEmpty()) {
119             validateOperations(interfaceDefinition.getType(), operationMap, model);
120             interfaceDefinition.setOperations(operationMap);
121         }
122         return interfaceDefinition;
123     }
124
125     private void validateOperations(final String interfaceType, final Map<String, OperationDataDefinition> operationMap, final String model) {
126         if (MapUtils.isEmpty(operationMap)) {
127             return;
128         }
129         Either<Map<String, InterfaceDefinition>, ResponseFormat> interfaceDefinitionMapEither = interfaceOperationBusinessLogic
130             .getAllInterfaceLifecycleTypes(model);
131         if (interfaceDefinitionMapEither.isRight() || MapUtils.isEmpty(interfaceDefinitionMapEither.left().value())) {
132             throw new ByActionStatusComponentException(ActionStatus.INTERFACE_UNKNOWN, interfaceType);
133         }
134         final Map<String, InterfaceDefinition> interfaceDefinitionMap = interfaceDefinitionMapEither.left().value();
135         final Optional<InterfaceDefinition> interfaceDefinitionOptional = interfaceDefinitionMap.entrySet().stream()
136             .filter(interfaceDefinitionEntry -> interfaceDefinitionEntry.getKey()
137                 .equalsIgnoreCase(UniqueIdBuilder.buildInterfaceTypeUid(model, interfaceType))).map(Entry::getValue).findFirst();
138         if (interfaceDefinitionOptional.isEmpty()) {
139             throw new ByActionStatusComponentException(ActionStatus.INTERFACE_UNKNOWN, interfaceType);
140         }
141         final InterfaceDefinition interfaceDefinition = interfaceDefinitionOptional.get();
142         operationMap.keySet().forEach(operation1 -> {
143             if (!interfaceDefinition.hasOperation(operation1)) {
144                 throw new ByActionStatusComponentException(ActionStatus.INTERFACE_OPERATION_NOT_DEFINED, operation1, interfaceType);
145             }
146         });
147     }
148
149     private Map<String, OperationDataDefinition> handleOperations(final Map<String, Object> interfaceDefinitionToscaMap) {
150         if (!interfaceDefinitionToscaMap.containsKey(OPERATIONS.getElementName())) {
151             return Collections.emptyMap();
152         }
153         final Map<String, Object> operationMap = (Map<String, Object>) interfaceDefinitionToscaMap.get(OPERATIONS.getElementName());
154         return operationMap.entrySet().stream()
155             .map(interfaceEntry -> createOperation(interfaceEntry.getKey(), (Map<String, Object>) interfaceEntry.getValue()))
156             .collect(Collectors.toMap(OperationDataDefinition::getName, operationDataDefinition -> operationDataDefinition));
157     }
158
159     private Map<String, OperationDataDefinition> handleLegacyOperations(final Map<String, Object> interfaceDefinitionToscaMap) {
160         final List<String> notALegacyOperationEntry = Arrays
161             .asList(OPERATIONS.getElementName(), TYPE.getElementName(), INPUTS.getElementName(), NOTIFICATIONS.getElementName());
162         return interfaceDefinitionToscaMap.entrySet().stream().filter(interfaceEntry -> !notALegacyOperationEntry.contains(interfaceEntry.getKey()))
163             .map(interfaceEntry -> createOperation(interfaceEntry.getKey(), (Map<String, Object>) interfaceEntry.getValue()))
164             .collect(Collectors.toMap(OperationDataDefinition::getName, operationDataDefinition -> operationDataDefinition));
165     }
166
167     private OperationDataDefinition createOperation(final String operationName, final Map<String, Object> operationDefinitionMap) {
168         final OperationDataDefinition operation = new OperationDataDefinition();
169         operation.setUniqueId(UUID.randomUUID().toString());
170         operation.setName(operationName);
171
172         if (MapUtils.isEmpty(operationDefinitionMap)) {
173             return operation;
174         }
175         Object operationDescription = operationDefinitionMap.get(DESCRIPTION.getElementName());
176         if (null != operationDescription) {
177             operation.setDescription(operationDescription.toString());
178         }
179         operation.setImplementation(handleOperationImplementation(operationDefinitionMap).orElse(new ArtifactDataDefinition()));
180         if (operationDefinitionMap.containsKey(INPUTS.getElementName())) {
181             final Map<String, Object> interfaceInputs = (Map<String, Object>) operationDefinitionMap.get(INPUTS.getElementName());
182             operation.setInputs(handleInterfaceOperationInputs(interfaceInputs));
183         }
184         if (operationDefinitionMap.containsKey(MILESTONES.getElementName())) {
185             final Map<String, Object> interfaceMilestones = (Map<String, Object>) operationDefinitionMap.get(MILESTONES.getElementName());
186             operation.setMilestones(handleInterfaceOperationMilestones(interfaceMilestones));
187         }
188         return operation;
189     }
190
191     private Map<String, MilestoneDataDefinition> handleInterfaceOperationMilestones(final Map<String, Object> interfaceMilestones) {
192         final Map<String, MilestoneDataDefinition> milestones = new HashMap<>();
193         for (final Entry<String, Object> interfaceInput : interfaceMilestones.entrySet()) {
194             final MilestoneDataDefinition operationMilestone = new MilestoneDataDefinition();
195             ListDataDefinition<ActivityDataDefinition> activities = handleMilestoneActivities(interfaceInput.getValue());
196             if (activities.isEmpty()) {
197                 throw new ByActionStatusComponentException(ActionStatus.INVALID_OPERATION_MILESTONE, interfaceInput.getKey());
198             }
199             operationMilestone.setActivities(activities);
200             milestones.put(interfaceInput.getKey(), operationMilestone);
201         }
202         return milestones;
203     }
204
205     private ListDataDefinition<ActivityDataDefinition> handleMilestoneActivities(final Object value) {
206         ListDataDefinition<ActivityDataDefinition> activities = new ListDataDefinition<>();
207         if (value instanceof Map) {
208             final LinkedHashMap<String, Object> activitiesValue = (LinkedHashMap<String, Object>) value;
209             if (activitiesValue.containsKey(ACTIVITIES.getElementName())) {
210                 final List<Object> milestoneActivities = (List<Object>) activitiesValue.get(ACTIVITIES.getElementName());
211                 for (Object activityValue : milestoneActivities) {
212                     ActivityDataDefinition activity = new ActivityDataDefinition();
213                     if (activityValue instanceof Map) {
214                         Map<String, String> activityMap = (Map<String, String>) activityValue;
215                         if (activityMap.containsKey(TYPE.getElementName()) && activityMap.containsKey(WORKFLOW.getElementName())) {
216                             activity.setType(activityMap.get(TYPE.getElementName()));
217                             activity.setWorkflow(activityMap.get(WORKFLOW.getElementName()));
218                             activities.add(activity);
219                         } else {
220                             return new ListDataDefinition<>();
221                         }
222                     } else {
223                         return new ListDataDefinition<>();
224                     }
225                 }
226             } else {
227                 return new ListDataDefinition<>();
228             }
229         }
230         return activities;
231     }
232
233     private ListDataDefinition<OperationInputDefinition> handleInterfaceOperationInputs(final Map<String, Object> interfaceInputs) {
234         final ListDataDefinition<OperationInputDefinition> inputs = new ListDataDefinition<>();
235         for (final Entry<String, Object> interfaceInput : interfaceInputs.entrySet()) {
236             final OperationInputDefinition operationInput = new OperationInputDefinition();
237             operationInput.setUniqueId(UUID.randomUUID().toString());
238             operationInput.setInputId(operationInput.getUniqueId());
239             operationInput.setName(interfaceInput.getKey());
240             handleInputToscaDefinition(interfaceInput.getKey(), interfaceInput.getValue(), operationInput);
241             inputs.add(operationInput);
242         }
243         return inputs;
244     }
245
246     private void handleInputToscaDefinition(final String inputName, final Object value, final OperationInputDefinition operationInput) {
247         if (value instanceof Map) {
248             final LinkedHashMap<String, Object> inputPropertyValue = (LinkedHashMap<String, Object>) value;
249             LOGGER.debug("Creating interface operation input '{}'", inputName);
250             if (inputPropertyValue.get(TYPE.getElementName()) != null) {
251                 final String type = inputPropertyValue.get(TYPE.getElementName()).toString();
252                 LOGGER.debug(WITH_ATTRIBUTE, TYPE.getElementName(), type);
253                 operationInput.setType(type);
254             }
255             if (inputPropertyValue.get(DESCRIPTION.getElementName()) != null) {
256                 final String description = inputPropertyValue.get(DESCRIPTION.getElementName()).toString();
257                 LOGGER.debug(WITH_ATTRIBUTE, DESCRIPTION.getElementName(), description);
258                 operationInput.setDescription(description);
259             }
260             if (inputPropertyValue.get(REQUIRED.getElementName()) != null) {
261                 final boolean required = Boolean.parseBoolean(inputPropertyValue.get(REQUIRED.getElementName()).toString());
262                 LOGGER.debug(WITH_ATTRIBUTE, REQUIRED.getElementName(), required);
263                 operationInput.setRequired(required);
264             }
265             if (inputPropertyValue.get(DEFAULT.getElementName()) != null) {
266                 final Gson gson = new Gson();
267                 final String json = gson.toJson(inputPropertyValue.get(DEFAULT.getElementName()));
268                 LOGGER.debug(WITH_ATTRIBUTE, DEFAULT.getElementName(), json);
269                 operationInput.setToscaDefaultValue(json);
270             }
271             if (inputPropertyValue.get(STATUS.getElementName()) != null) {
272                 final String status = inputPropertyValue.get(STATUS.getElementName()).toString();
273                 LOGGER.debug(WITH_ATTRIBUTE, STATUS.getElementName(), status);
274                 operationInput.setStatus(status);
275             }
276             return;
277         }
278         if (value instanceof String) {
279             final String stringValue = (String) value;
280             operationInput.setDefaultValue(stringValue);
281             operationInput.setToscaDefaultValue(stringValue);
282             operationInput.setValue(stringValue);
283         }
284     }
285
286     @SuppressWarnings("unchecked")
287     private Optional<ArtifactDataDefinition> handleOperationImplementation(final Map<String, Object> operationDefinitionMap) {
288         if (!operationDefinitionMap.containsKey(IMPLEMENTATION.getElementName())) {
289             return Optional.empty();
290         }
291         final ArtifactDataDefinition artifactDataDefinition = new ArtifactDataDefinition();
292         if (operationDefinitionMap.get(IMPLEMENTATION.getElementName()) instanceof Map &&
293             ((Map) operationDefinitionMap.get(IMPLEMENTATION.getElementName())).containsKey("primary")) {
294             Map<String, Object> implDetails = (Map) ((Map) operationDefinitionMap.get(IMPLEMENTATION.getElementName())).get("primary");
295
296             if (implDetails.get("file") != null) {
297                 final String file = implDetails.get("file").toString();
298                 artifactDataDefinition.setArtifactName(generateArtifactName(file));
299             }
300             if (implDetails.get("type") != null) {
301                 artifactDataDefinition.setArtifactType(implDetails.get("type").toString());
302             }
303             if (implDetails.get("artifact_version") != null) {
304                 artifactDataDefinition.setArtifactVersion(implDetails.get("artifact_version").toString());
305             }
306
307             if (implDetails.get("properties") instanceof Map) {
308                 List<PropertyDataDefinition> operationProperties =
309                     artifactDataDefinition.getProperties() == null ? new ArrayList<>() : artifactDataDefinition.getProperties();
310                 Map<String, Object> properties = (Map<String, Object>) implDetails.get("properties");
311                 properties.forEach((k, v) -> {
312                     ToscaPropertyType type = getTypeFromObject(v);
313                     if (type != null) {
314                         PropertyDataDefinition propertyDef = new PropertyDataDefinition();
315                         propertyDef.setName(k);
316                         propertyDef.setType(type.getType());
317                         propertyDef.setValue(v.toString());
318                         if (type.equals(ToscaPropertyType.LIST)) {
319                             Gson gson = new Gson();
320                             propertyDef.setValue(gson.toJson(v));
321                             PropertyDataDefinition pdd = new PropertyDataDefinition();
322                             pdd.setType("string");
323                             SchemaDefinition sd = new SchemaDefinition();
324                             sd.setProperty(pdd);
325                             propertyDef.setSchema(sd);
326                         }
327                         artifactDataDefinition.addProperty(propertyDef);
328                     }
329                 });
330             }
331         }
332
333         if (operationDefinitionMap.get(IMPLEMENTATION.getElementName()) instanceof Map &&
334             ((Map) operationDefinitionMap.get(IMPLEMENTATION.getElementName())).containsKey("timeout")) {
335             final Object timeOut = ((Map) operationDefinitionMap.get(IMPLEMENTATION.getElementName())).get("timeout");
336             artifactDataDefinition.setTimeout((Integer)timeOut);
337         }
338
339         if (operationDefinitionMap.get(IMPLEMENTATION.getElementName()) instanceof String) {
340             final String implementation = (String) operationDefinitionMap.get(IMPLEMENTATION.getElementName());
341             artifactDataDefinition.setArtifactName(generateArtifactName(implementation));
342         }
343         return Optional.of(artifactDataDefinition);
344     }
345
346     private String generateArtifactName(final String name) {
347         if (OperationArtifactUtil.artifactNameIsALiteralValue(name)) {
348             return name;
349         } else {
350             return QUOTE + name + QUOTE;
351         }
352     }
353
354     private ToscaPropertyType getTypeFromObject(final Object value) {
355         if (value instanceof String) {
356             return ToscaPropertyType.STRING;
357         }
358         if (value instanceof Integer) {
359             return ToscaPropertyType.INTEGER;
360         }
361         if (value instanceof Boolean) {
362             return ToscaPropertyType.BOOLEAN;
363         }
364         if (value instanceof Float || value instanceof Double) {
365             return ToscaPropertyType.FLOAT;
366         }
367         if (value instanceof List) {
368             return ToscaPropertyType.LIST;
369         }
370         return null;
371     }
372
373
374     private Map<String, InputDefinition> handleInputs(final Map<String, Object> interfaceDefinitionToscaMap) {
375         if (!interfaceDefinitionToscaMap.containsKey(INPUTS.getElementName())) {
376             return Collections.emptyMap();
377         }
378         final Either<Map<String, InputDefinition>, ResultStatusEnum> inputMapEither = ImportUtils.getInputs(interfaceDefinitionToscaMap);
379         if (inputMapEither.isRight()) {
380             return Collections.emptyMap();
381         }
382         return inputMapEither.left().value();
383     }
384 }