ecab6402c6944f1e5a87725ffe992abd86f5cac8
[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(componentsUtils.getResponseFormat(ActionStatus.OUTPUT_NAME_ALREADY_EXIST, status.getRight()));
140             }
141             result = attributeDeclarationOrchestrator.declareAttributesToOutputs(component, componentInstOutputsMapUi)
142                 .left().bind(outputsToCreate -> prepareOutputsForCreation(userId, componentId, outputsToCreate))
143                 .right().map(componentsUtils::getResponseFormat);
144             return result;
145         } catch (final ByResponseFormatComponentException e) {
146             log.error("#createMultipleOutputs: Exception thrown: ", e);
147             result = Either.right(e.getResponseFormat());
148             return result;
149         } finally {
150             if (!inTransaction) {
151                 if (result.isRight()) {
152                     log.debug(GOING_TO_EXECUTE_ROLLBACK_ON_CREATE_OUTPUTS);
153                     janusGraphDao.rollback();
154                 } else {
155                     log.debug(GOING_TO_EXECUTE_COMMIT_ON_CREATE_OUTPUTS);
156                     janusGraphDao.commit();
157                 }
158             }
159             // unlock resource
160             if (shouldLockComp && component != null) {
161                 graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
162             }
163         }
164     }
165
166     private ImmutablePair<StorageOperationStatus, String> validateOutputName(final Component component,
167                                                                             final ComponentInstOutputsMap componentInstOutputsMapUi) {
168         final Map<String, List<ComponentInstanceAttribOutput>> outputDeclaredProperties = new HashMap<>();
169         if (MapUtils.isNotEmpty(componentInstOutputsMapUi.getComponentInstanceOutputsMap())) {
170             outputDeclaredProperties.putAll(componentInstOutputsMapUi.getComponentInstanceOutputsMap());
171         } else if (MapUtils.isNotEmpty(componentInstOutputsMapUi.getComponentInstanceAttributes())) {
172             outputDeclaredProperties.putAll(componentInstOutputsMapUi.getComponentInstanceAttributes());
173         }
174         if (MapUtils.isNotEmpty(outputDeclaredProperties) && CollectionUtils.isNotEmpty(component.getOutputs())) {
175             for (final List<ComponentInstanceAttribOutput> componentInstancePropOutputs : outputDeclaredProperties.values()) {
176                 for (final ComponentInstanceAttribOutput componentInstancePropOutput : componentInstancePropOutputs) {
177                     final Optional<OutputDefinition> outputDefinition = component.getOutputs().stream()
178                         .filter(output -> output.getName().equals(componentInstancePropOutput.getOutputName())
179                             || output.getName().equals(componentInstancePropOutput.getName())).findAny();
180                     if (outputDefinition.isPresent()) {
181                         return new ImmutablePair<>(StorageOperationStatus.INVALID_VALUE, outputDefinition.get().getName());
182                     }
183                 }
184             }
185         }
186         return new ImmutablePair<>(StorageOperationStatus.OK, StringUtils.EMPTY);
187     }
188
189     private Component getAndValidateComponentForCreate(final String userId, final String componentId,
190                                                        final ComponentTypeEnum componentType,
191                                                        final boolean shouldLockComp) {
192         final ComponentParametersView componentParametersView = getBaseComponentParametersView();
193         final Component component = validateComponentExists(componentId, componentType, componentParametersView);
194         if (shouldLockComp) {
195             // lock the component
196             lockComponent(component, CREATE_OUTPUT);
197         }
198         validateCanWorkOnComponent(component, userId);
199         return component;
200     }
201
202     private Either<List<OutputDefinition>, StorageOperationStatus> prepareOutputsForCreation(final String userId, final String cmptId,
203                                                                                              final List<OutputDefinition> outputsToCreate) {
204         final Map<String, OutputDefinition> outputsToPersist = MapUtil.toMap(outputsToCreate, OutputDefinition::getName);
205         assignOwnerIdToOutputs(userId, outputsToPersist);
206         final var statusEither = toscaOperationFacade.addOutputsToComponent(outputsToPersist, cmptId);
207         if (statusEither.isRight()) {
208             return statusEither;
209         }
210         return statusEither.left().map(persistedOutputs -> outputsToCreate);
211     }
212
213     private void assignOwnerIdToOutputs(final String userId, final Map<String, OutputDefinition> outputsToCreate) {
214         outputsToCreate.values().forEach(outputDefinition -> outputDefinition.setOwnerId(userId));
215     }
216
217     private ComponentParametersView getBaseComponentParametersView() {
218         final ComponentParametersView componentParametersView = new ComponentParametersView();
219         componentParametersView.disableAll();
220         componentParametersView.setIgnoreOutputs(false);
221         componentParametersView.setIgnoreAttributes(false);
222         componentParametersView.setIgnoreComponentInstances(false);
223         componentParametersView.setIgnoreComponentInstancesOutputs(false);
224         componentParametersView.setIgnoreComponentInstancesAttributes(false);
225         componentParametersView.setIgnoreComponentInstancesProperties(false);
226         componentParametersView.setIgnoreUsers(false);
227         return componentParametersView;
228     }
229
230     /**
231      * Delete output from component
232      *
233      * @param componentId
234      * @param userId
235      * @param outputId
236      * @return
237      */
238     public OutputDefinition deleteOutput(final String componentId, final String userId, final String outputId) {
239         if (log.isDebugEnabled()) {
240             log.debug("Going to delete output id: {}", outputId);
241         }
242         validateUserExists(userId);
243         final ComponentParametersView componentParametersView = getBaseComponentParametersView();
244         componentParametersView.setIgnoreAttributes(false);
245         final Either<org.openecomp.sdc.be.model.Component, StorageOperationStatus> componentEither = toscaOperationFacade
246             .getToscaElement(componentId, componentParametersView);
247         if (componentEither.isRight()) {
248             throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(componentEither.right().value()));
249         }
250         final org.openecomp.sdc.be.model.Component component = componentEither.left().value();
251         // Validate outputId is child of the component
252         final Optional<OutputDefinition> optionalOutput = component.getOutputs().stream().
253             // filter by ID
254                 filter(output -> output.getUniqueId().equals(outputId)).
255             // Get the output
256                 findAny();
257         if (!optionalOutput.isPresent()) {
258             throw new ByActionStatusComponentException(ActionStatus.OUTPUT_IS_NOT_CHILD_OF_COMPONENT, outputId, componentId);
259         }
260         final OutputDefinition outputForDelete = optionalOutput.get();
261         // Lock component
262         lockComponent(componentId, component, "deleteOutput");
263         // Delete output operations
264         boolean failed = false;
265         try {
266             final StorageOperationStatus status = toscaOperationFacade.deleteOutputOfResource(component, outputForDelete.getName());
267             if (status != StorageOperationStatus.OK) {
268                 log.debug("Component id: {} delete output id: {} failed", componentId, outputId);
269                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(status), component.getName());
270             }
271             final StorageOperationStatus storageOperationStatus = attributeDeclarationOrchestrator
272                 .unDeclareAttributesAsOutputs(component, outputForDelete);
273             if (storageOperationStatus != StorageOperationStatus.OK) {
274                 log.debug("Component id: {} update attributes declared as output for outputId: {} failed", componentId, outputId);
275                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(storageOperationStatus), component.getName());
276             }
277             return outputForDelete;
278         } catch (final ComponentException e) {
279             failed = true;
280             throw e;
281         } finally {
282             unlockComponent(failed, component);
283         }
284     }
285
286     public Either<List<OutputDefinition>, ResponseFormat> createOutputsInGraph(final Map<String, OutputDefinition> outputs,
287                                                                                final Component component,
288                                                                                final String userId) {
289
290         final List<OutputDefinition> result = new ArrayList<>();
291         for (final Map.Entry<String, OutputDefinition> outputDefinition : outputs.entrySet()) {
292             final var outputDefinitionValue = outputDefinition.getValue();
293             outputDefinitionValue.setName(outputDefinition.getKey());
294
295             final String value = outputDefinitionValue.getValue();
296             if (value != null) {
297                 final List<String> getAttribute = (List<String>) ImportUtils.loadYamlAsStrictMap(value)
298                     .get(ToscaGetFunctionType.GET_ATTRIBUTE.getFunctionName());
299                 if (getAttribute.size() == 2) {
300                     final var optionalComponentInstance = component.getComponentInstanceByName(getAttribute.get(0));
301                     if (optionalComponentInstance.isPresent()) {
302                         final var createdOutputs
303                             = createOutputs(component.getUniqueId(), userId, getAttribute.get(1), optionalComponentInstance.get());
304                         if (createdOutputs.isRight()) {
305                             return Either.right((createdOutputs.right().value()));
306                         }
307                         result.addAll(createdOutputs.left().value());
308                     } else {
309                         // From SELF
310                         outputDefinitionValue.setInstanceUniqueId(component.getUniqueId());
311                     }
312                 }
313             }
314         }
315         return Either.left(result);
316
317     }
318
319     private Either<List<OutputDefinition>, ResponseFormat> createOutputs(final String componentUniqueId, final String userId,
320                                                                          final String attributeName,
321                                                                          final ComponentInstance componentInstance) {
322         // From Instance
323         final var componentInstanceAttributes = componentInstance.getAttributes();
324         if (CollectionUtils.isNotEmpty(componentInstanceAttributes)) {
325             final var componentInstanceAttributeOptional = componentInstanceAttributes.stream()
326                 .filter(ad -> ad.getName().equals(attributeName)).map(ComponentInstanceAttribute::new).findFirst();
327             if (componentInstanceAttributeOptional.isPresent()) {
328                 final var componentInstOutputsMap = new ComponentInstOutputsMap();
329                 componentInstOutputsMap.setComponentInstanceAttributes(Collections.singletonMap(componentInstance.getUniqueId(),
330                     Collections.singletonList(new ComponentInstanceAttribOutput(componentInstanceAttributeOptional.get()))));
331                 return createMultipleOutputs(userId, componentUniqueId, ComponentTypeEnum.SERVICE,
332                     componentInstOutputsMap, true, false);
333             }
334         }
335         final List<PropertyDefinition> componentInstanceProperties = componentInstance.getProperties();
336         if (CollectionUtils.isNotEmpty(componentInstanceProperties)) {
337             final Optional<PropertyDefinition> componentInstancePropertyOptional = componentInstanceProperties.stream()
338                 .filter(prop -> prop.getName().equals(attributeName)).findFirst();
339             if (componentInstancePropertyOptional.isPresent()) {
340                 PropertyDefinition propertyDefinition = componentInstancePropertyOptional.get();
341                 final ComponentInstOutputsMap componentInstOutputsMap = new ComponentInstOutputsMap();
342                 ComponentInstanceAttribOutput attribute = getComponentInstanceAttribOutput(propertyDefinition);
343                 componentInstOutputsMap.setComponentInstanceAttributes(Collections.singletonMap(componentInstance.getUniqueId(),
344                     Collections.singletonList(new ComponentInstanceAttribOutput(attribute))));
345                 return createMultipleOutputs(userId, componentUniqueId, ComponentTypeEnum.SERVICE, componentInstOutputsMap, true, false);
346             }
347         }
348         return Either.left(new ArrayList<>());
349     }
350
351     @NotNull
352     private ComponentInstanceAttribOutput getComponentInstanceAttribOutput(PropertyDefinition propertyDefinition) {
353         ComponentInstanceAttribOutput attribute = new ComponentInstanceAttribOutput();
354         attribute.setParentUniqueId(propertyDefinition.getParentUniqueId());
355         attribute.setName(propertyDefinition.getName());
356         attribute.setOwnerId(propertyDefinition.getOwnerId());
357         attribute.setType(propertyDefinition.getType());
358         attribute.setSchema(propertyDefinition.getSchema());
359         attribute.setUniqueId(propertyDefinition.getUniqueId());
360         return attribute;
361     }
362
363 }