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