Fix bugs in attribute outputs page
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / OutputsBusinessLogic.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2021, Nordix Foundation. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.openecomp.sdc.be.components.impl;
22
23 import fj.data.Either;
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.HashMap;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Optional;
30 import org.apache.commons.collections.MapUtils;
31 import org.apache.commons.collections4.CollectionUtils;
32 import org.apache.commons.lang3.StringUtils;
33 import org.apache.commons.lang3.tuple.ImmutablePair;
34 import org.jetbrains.annotations.NotNull;
35 import org.openecomp.sdc.be.components.attribute.AttributeDeclarationOrchestrator;
36 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
37 import org.openecomp.sdc.be.components.impl.exceptions.ByResponseFormatComponentException;
38 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
39 import org.openecomp.sdc.be.components.validation.ComponentValidations;
40 import org.openecomp.sdc.be.dao.api.ActionStatus;
41 import org.openecomp.sdc.be.dao.utils.MapUtil;
42 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
43 import org.openecomp.sdc.be.datatypes.tosca.ToscaGetFunctionType;
44 import org.openecomp.sdc.be.model.Component;
45 import org.openecomp.sdc.be.model.ComponentInstOutputsMap;
46 import org.openecomp.sdc.be.model.ComponentInstance;
47 import org.openecomp.sdc.be.model.ComponentInstanceAttribOutput;
48 import org.openecomp.sdc.be.model.ComponentInstanceAttribute;
49 import org.openecomp.sdc.be.model.ComponentInstanceOutput;
50 import org.openecomp.sdc.be.model.ComponentParametersView;
51 import org.openecomp.sdc.be.model.OutputDefinition;
52 import org.openecomp.sdc.be.model.PropertyDefinition;
53 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ArtifactsOperations;
54 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.InterfaceOperation;
55 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
56 import org.openecomp.sdc.be.model.operations.api.IGroupInstanceOperation;
57 import org.openecomp.sdc.be.model.operations.api.IGroupOperation;
58 import org.openecomp.sdc.be.model.operations.api.IGroupTypeOperation;
59 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
60 import org.openecomp.sdc.be.model.operations.impl.InterfaceLifecycleOperation;
61 import org.openecomp.sdc.common.log.elements.LoggerSupportability;
62 import org.openecomp.sdc.common.log.enums.LoggerSupportabilityActions;
63 import org.openecomp.sdc.common.log.enums.StatusCode;
64 import org.openecomp.sdc.common.log.wrappers.Logger;
65 import org.openecomp.sdc.exception.ResponseFormat;
66 import org.springframework.beans.factory.annotation.Autowired;
67 import org.springframework.http.HttpStatus;
68
69 @org.springframework.stereotype.Component("outputsBusinessLogic")
70 public class OutputsBusinessLogic extends BaseBusinessLogic {
71
72     private static final String CREATE_OUTPUT = "CreateOutput";
73     private static final Logger log = Logger.getLogger(OutputsBusinessLogic.class);
74     private static final String FAILED_TO_FOUND_COMPONENT_ERROR = "Failed to found component {}, error: {}";
75     private static final String GOING_TO_EXECUTE_ROLLBACK_ON_CREATE_OUTPUTS = "Going to execute rollback on create outputs.";
76     private static final String GOING_TO_EXECUTE_COMMIT_ON_CREATE_OUTPUTS = "Going to execute commit on create outputs.";
77     private static final LoggerSupportability loggerSupportability = LoggerSupportability.getLogger(OutputsBusinessLogic.class);
78     private static final String FAILED_TO_FOUND_COMPONENT_INSTANCE_OUTPUTS_COMPONENT_INSTANCE_ID =
79         "Failed to found component instance outputs componentInstanceId: {}";
80     private static final String FAILED_TO_FOUND_COMPONENT_INSTANCE_OUTPUTS_ERROR = "Failed to found component instance outputs {}, error: {}";
81     private final AttributeDeclarationOrchestrator attributeDeclarationOrchestrator;
82
83     @Autowired
84     public OutputsBusinessLogic(final IElementOperation elementDao, final IGroupOperation groupOperation,
85                                 final IGroupInstanceOperation groupInstanceOperation, final IGroupTypeOperation groupTypeOperation,
86                                 final InterfaceOperation interfaceOperation, final InterfaceLifecycleOperation interfaceLifecycleTypeOperation,
87                                 final AttributeDeclarationOrchestrator attributeDeclarationOrchestrator,
88                                 final ArtifactsOperations artifactToscaOperation) {
89         super(elementDao, groupOperation, groupInstanceOperation, groupTypeOperation, interfaceOperation, interfaceLifecycleTypeOperation,
90             artifactToscaOperation);
91         this.attributeDeclarationOrchestrator = attributeDeclarationOrchestrator;
92     }
93
94     public Either<List<ComponentInstanceOutput>, ResponseFormat> getComponentInstanceOutputs(final String userId, final String componentId,
95                                                                                              final String componentInstanceId) {
96         validateUserExists(userId);
97         final ComponentParametersView filters = new ComponentParametersView();
98         filters.disableAll();
99         filters.setIgnoreOutputs(false);
100         filters.setIgnoreComponentInstances(false);
101         filters.setIgnoreComponentInstancesOutputs(false);
102         final Either<Component, StorageOperationStatus> getComponentEither = toscaOperationFacade.getToscaElement(componentId, filters);
103         if (getComponentEither.isRight()) {
104             ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(getComponentEither.right().value());
105             log.debug(FAILED_TO_FOUND_COMPONENT_ERROR, componentId, actionStatus);
106             return Either.right(componentsUtils.getResponseFormat(actionStatus));
107         }
108         final Component component = getComponentEither.left().value();
109         if (!ComponentValidations.validateComponentInstanceExist(component, componentInstanceId)) {
110             final ActionStatus actionStatus = ActionStatus.COMPONENT_INSTANCE_NOT_FOUND;
111             log.debug(FAILED_TO_FOUND_COMPONENT_INSTANCE_OUTPUTS_ERROR, componentInstanceId, actionStatus);
112             loggerSupportability.log(LoggerSupportabilityActions.CREATE_OUTPUTS, component.getComponentMetadataForSupportLog(), StatusCode.ERROR,
113                 FAILED_TO_FOUND_COMPONENT_INSTANCE_OUTPUTS_COMPONENT_INSTANCE_ID, componentInstanceId);
114             return Either.right(componentsUtils.getResponseFormat(actionStatus));
115         }
116         final Map<String, List<ComponentInstanceOutput>> ciOutputs = Optional.ofNullable(component.getComponentInstancesOutputs())
117             .orElse(Collections.emptyMap());
118         return Either.left(ciOutputs.getOrDefault(componentInstanceId, Collections.emptyList()));
119     }
120
121     @Override
122     public Either<List<OutputDefinition>, ResponseFormat> declareAttributes(final String userId, final String componentId,
123                                                                             final ComponentTypeEnum componentTypeEnum,
124                                                                             final ComponentInstOutputsMap componentInstOutputsMap) {
125         return createMultipleOutputs(userId, componentId, componentTypeEnum, componentInstOutputsMap, true, false);
126     }
127
128     private Either<List<OutputDefinition>, ResponseFormat> createMultipleOutputs(final String userId, final String componentId,
129                                                                                  final ComponentTypeEnum componentType,
130                                                                                  final ComponentInstOutputsMap componentInstOutputsMapUi,
131                                                                                  final boolean shouldLockComp, final boolean inTransaction) {
132         Either<List<OutputDefinition>, ResponseFormat> result = Either.right(new ResponseFormat(HttpStatus.BAD_REQUEST.value()));
133         org.openecomp.sdc.be.model.Component component = null;
134         try {
135             validateUserExists(userId);
136             component = getAndValidateComponentForCreate(userId, componentId, componentType, shouldLockComp);
137             ImmutablePair<StorageOperationStatus, String> status = validateOutputName(component, componentInstOutputsMapUi);
138             if (status.getLeft() != StorageOperationStatus.OK) {
139                 throw new ByResponseFormatComponentException(
140                     componentsUtils.getResponseFormat(ActionStatus.OUTPUT_NAME_ALREADY_EXIST, status.getRight()));
141             }
142             result = attributeDeclarationOrchestrator.declareAttributesToOutputs(component, componentInstOutputsMapUi)
143                 .left().bind(outputsToCreate -> prepareOutputsForCreation(userId, componentId, outputsToCreate))
144                 .right().map(componentsUtils::getResponseFormat);
145             return result;
146         } catch (final ByResponseFormatComponentException e) {
147             log.error("#createMultipleOutputs: Exception thrown: ", e);
148             result = Either.right(e.getResponseFormat());
149             return result;
150         } finally {
151             if (!inTransaction) {
152                 if (result.isRight()) {
153                     log.debug(GOING_TO_EXECUTE_ROLLBACK_ON_CREATE_OUTPUTS);
154                     janusGraphDao.rollback();
155                 } else {
156                     log.debug(GOING_TO_EXECUTE_COMMIT_ON_CREATE_OUTPUTS);
157                     janusGraphDao.commit();
158                 }
159             }
160             // unlock resource
161             if (shouldLockComp && component != null) {
162                 graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
163             }
164         }
165     }
166
167     private ImmutablePair<StorageOperationStatus, String> validateOutputName(final Component component,
168                                                                              final ComponentInstOutputsMap componentInstOutputsMapUi) {
169         final Map<String, List<ComponentInstanceAttribOutput>> outputDeclaredProperties = new HashMap<>();
170         if (MapUtils.isNotEmpty(componentInstOutputsMapUi.getComponentInstanceOutputsMap())) {
171             outputDeclaredProperties.putAll(componentInstOutputsMapUi.getComponentInstanceOutputsMap());
172         } else if (MapUtils.isNotEmpty(componentInstOutputsMapUi.getComponentInstanceAttributes())) {
173             outputDeclaredProperties.putAll(componentInstOutputsMapUi.getComponentInstanceAttributes());
174         }
175         if (MapUtils.isNotEmpty(outputDeclaredProperties) && CollectionUtils.isNotEmpty(component.getOutputs())) {
176             for (final List<ComponentInstanceAttribOutput> componentInstancePropOutputs : outputDeclaredProperties.values()) {
177                 for (final ComponentInstanceAttribOutput componentInstancePropOutput : componentInstancePropOutputs) {
178                     final Optional<OutputDefinition> outputDefinition = component.getOutputs().stream()
179                         .filter(output -> output.getName().equals(componentInstancePropOutput.getOutputName())
180                             || output.getName().equals(componentInstancePropOutput.getName())).findAny();
181                     if (outputDefinition.isPresent()) {
182                         return new ImmutablePair<>(StorageOperationStatus.INVALID_VALUE, outputDefinition.get().getName());
183                     }
184                 }
185             }
186         }
187         return new ImmutablePair<>(StorageOperationStatus.OK, StringUtils.EMPTY);
188     }
189
190     private Component getAndValidateComponentForCreate(final String userId, final String componentId,
191                                                        final ComponentTypeEnum componentType,
192                                                        final boolean shouldLockComp) {
193         final ComponentParametersView componentParametersView = getBaseComponentParametersView();
194         final Component component = validateComponentExists(componentId, componentType, componentParametersView);
195         if (shouldLockComp) {
196             // lock the component
197             lockComponent(component, CREATE_OUTPUT);
198         }
199         validateCanWorkOnComponent(component, userId);
200         return component;
201     }
202
203     private Either<List<OutputDefinition>, StorageOperationStatus> prepareOutputsForCreation(final String userId, final String cmptId,
204                                                                                              final List<OutputDefinition> outputsToCreate) {
205         final Map<String, OutputDefinition> outputsToPersist = MapUtil.toMap(outputsToCreate, OutputDefinition::getName);
206         assignOwnerIdToOutputs(userId, outputsToPersist);
207         final var statusEither = toscaOperationFacade.addOutputsToComponent(outputsToPersist, cmptId);
208         if (statusEither.isRight()) {
209             return statusEither;
210         }
211         return statusEither.left().map(persistedOutputs -> outputsToCreate);
212     }
213
214     private void assignOwnerIdToOutputs(final String userId, final Map<String, OutputDefinition> outputsToCreate) {
215         outputsToCreate.values().forEach(outputDefinition -> outputDefinition.setOwnerId(userId));
216     }
217
218     private ComponentParametersView getBaseComponentParametersView() {
219         final ComponentParametersView componentParametersView = new ComponentParametersView();
220         componentParametersView.disableAll();
221         componentParametersView.setIgnoreOutputs(false);
222         componentParametersView.setIgnoreAttributes(false);
223         componentParametersView.setIgnoreComponentInstances(false);
224         componentParametersView.setIgnoreComponentInstancesOutputs(false);
225         componentParametersView.setIgnoreComponentInstancesAttributes(false);
226         componentParametersView.setIgnoreComponentInstancesProperties(false);
227         componentParametersView.setIgnoreUsers(false);
228         return componentParametersView;
229     }
230
231     /**
232      * Delete output from component
233      *
234      * @param componentId
235      * @param userId
236      * @param outputId
237      * @return
238      */
239     public OutputDefinition deleteOutput(final String componentId, final String userId, final String outputId) {
240         if (log.isDebugEnabled()) {
241             log.debug("Going to delete output id: {}", outputId);
242         }
243         validateUserExists(userId);
244         final ComponentParametersView componentParametersView = getBaseComponentParametersView();
245         componentParametersView.setIgnoreAttributes(false);
246         final Either<org.openecomp.sdc.be.model.Component, StorageOperationStatus> componentEither = toscaOperationFacade
247             .getToscaElement(componentId, componentParametersView);
248         if (componentEither.isRight()) {
249             throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(componentEither.right().value()));
250         }
251         final org.openecomp.sdc.be.model.Component component = componentEither.left().value();
252         // Validate outputId is child of the component
253         final Optional<OutputDefinition> optionalOutput = component.getOutputs().stream().
254             // filter by ID
255                 filter(output -> output.getUniqueId().equals(outputId)).
256             // Get the output
257                 findAny();
258         if (!optionalOutput.isPresent()) {
259             throw new ByActionStatusComponentException(ActionStatus.OUTPUT_IS_NOT_CHILD_OF_COMPONENT, outputId, componentId);
260         }
261         final OutputDefinition outputForDelete = optionalOutput.get();
262         // Lock component
263         lockComponent(componentId, component, "deleteOutput");
264         // Delete output operations
265         boolean failed = false;
266         try {
267             final StorageOperationStatus status = toscaOperationFacade.deleteOutputOfResource(component, outputForDelete.getName());
268             if (status != StorageOperationStatus.OK) {
269                 log.debug("Component id: {} delete output id: {} failed", componentId, outputId);
270                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(status), component.getName());
271             }
272             final StorageOperationStatus storageOperationStatus = attributeDeclarationOrchestrator
273                 .unDeclareAttributesAsOutputs(component, outputForDelete);
274             if (storageOperationStatus != StorageOperationStatus.OK) {
275                 log.debug("Component id: {} update attributes declared as output for outputId: {} failed", componentId, outputId);
276                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(storageOperationStatus), component.getName());
277             }
278             return outputForDelete;
279         } catch (final ComponentException e) {
280             failed = true;
281             throw e;
282         } finally {
283             unlockComponent(failed, component);
284         }
285     }
286
287     public Either<List<OutputDefinition>, ResponseFormat> createOutputsInGraph(final Map<String, OutputDefinition> outputs,
288                                                                                final Component component,
289                                                                                final String userId) {
290
291         final List<OutputDefinition> result = new ArrayList<>();
292         for (final Map.Entry<String, OutputDefinition> outputDefinition : outputs.entrySet()) {
293             final var outputDefinitionValue = outputDefinition.getValue();
294             outputDefinitionValue.setName(outputDefinition.getKey());
295
296             final String value = outputDefinitionValue.getValue();
297             if (value != null) {
298                 final List<String> getAttribute = (List<String>) ImportUtils.loadYamlAsStrictMap(value)
299                     .get(ToscaGetFunctionType.GET_ATTRIBUTE.getFunctionName());
300                 if (getAttribute.size() == 2) {
301                     final var optionalComponentInstance = component.getComponentInstanceByName(getAttribute.get(0));
302                     if (optionalComponentInstance.isPresent()) {
303                         final var createdOutputs
304                             = createOutputs(component.getUniqueId(), userId, getAttribute.get(1), optionalComponentInstance.get(),
305                             outputDefinitionValue.getName());
306                         if (createdOutputs.isRight()) {
307                             return Either.right((createdOutputs.right().value()));
308                         }
309                         result.addAll(createdOutputs.left().value());
310                     } else {
311                         // From SELF
312                         outputDefinitionValue.setInstanceUniqueId(component.getUniqueId());
313                     }
314                 }
315             }
316         }
317         return Either.left(result);
318
319     }
320
321     private Either<List<OutputDefinition>, ResponseFormat> createOutputs(final String componentUniqueId, final String userId,
322                                                                          final String attributeName,
323                                                                          final ComponentInstance componentInstance,
324                                                                          final String outputName) {
325         // From Instance
326         final var componentInstanceAttributes = componentInstance.getAttributes();
327         if (CollectionUtils.isNotEmpty(componentInstanceAttributes)) {
328             final var componentInstanceAttributeOptional = componentInstanceAttributes.stream()
329                 .filter(ad -> ad.getName().equals(attributeName)).map(ComponentInstanceAttribute::new).findFirst();
330             if (componentInstanceAttributeOptional.isPresent()) {
331                 final var componentInstOutputsMap = new ComponentInstOutputsMap();
332                 componentInstOutputsMap.setComponentInstanceAttributes(Collections.singletonMap(componentInstance.getUniqueId(),
333                     Collections.singletonList(new ComponentInstanceAttribOutput(componentInstanceAttributeOptional.get(), outputName))));
334                 return createMultipleOutputs(userId, componentUniqueId, ComponentTypeEnum.SERVICE,
335                     componentInstOutputsMap, true, false);
336             }
337         }
338         final List<PropertyDefinition> componentInstanceProperties = componentInstance.getProperties();
339         if (CollectionUtils.isNotEmpty(componentInstanceProperties)) {
340             final Optional<PropertyDefinition> componentInstancePropertyOptional = componentInstanceProperties.stream()
341                 .filter(prop -> prop.getName().equals(attributeName)).findFirst();
342             if (componentInstancePropertyOptional.isPresent()) {
343                 PropertyDefinition propertyDefinition = componentInstancePropertyOptional.get();
344                 final ComponentInstOutputsMap componentInstOutputsMap = new ComponentInstOutputsMap();
345                 ComponentInstanceAttribOutput attribute = getComponentInstanceAttribOutput(propertyDefinition);
346                 componentInstOutputsMap.setComponentInstanceAttributes(Collections.singletonMap(componentInstance.getUniqueId(),
347                     Collections.singletonList(new ComponentInstanceAttribOutput(attribute, outputName))));
348                 return createMultipleOutputs(userId, componentUniqueId, ComponentTypeEnum.SERVICE, componentInstOutputsMap, true, false);
349             }
350         }
351         return Either.left(new ArrayList<>());
352     }
353
354     @NotNull
355     private ComponentInstanceAttribOutput getComponentInstanceAttribOutput(PropertyDefinition propertyDefinition) {
356         ComponentInstanceAttribOutput attribute = new ComponentInstanceAttribOutput();
357         attribute.setParentUniqueId(propertyDefinition.getParentUniqueId());
358         attribute.setName(propertyDefinition.getName());
359         attribute.setOwnerId(propertyDefinition.getOwnerId());
360         attribute.setType(propertyDefinition.getType());
361         attribute.setSchema(propertyDefinition.getSchema());
362         attribute.setUniqueId(propertyDefinition.getUniqueId());
363         return attribute;
364     }
365
366 }