Provide user to specify the ouput name while declaring the atrributes
[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).left()
142                 .bind(outputsToCreate -> prepareOutputsForCreation(userId, componentId, outputsToCreate)).right()
143                 .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.setIgnoreUsers(false);
226         return componentParametersView;
227     }
228
229     /**
230      * Delete output from component
231      *
232      * @param componentId
233      * @param userId
234      * @param outputId
235      * @return
236      */
237     public OutputDefinition deleteOutput(final String componentId, final String userId, final String outputId) {
238         if (log.isDebugEnabled()) {
239             log.debug("Going to delete output id: {}", outputId);
240         }
241         validateUserExists(userId);
242         final ComponentParametersView componentParametersView = getBaseComponentParametersView();
243         componentParametersView.setIgnoreAttributes(false);
244         final Either<org.openecomp.sdc.be.model.Component, StorageOperationStatus> componentEither = toscaOperationFacade
245             .getToscaElement(componentId, componentParametersView);
246         if (componentEither.isRight()) {
247             throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(componentEither.right().value()));
248         }
249         final org.openecomp.sdc.be.model.Component component = componentEither.left().value();
250         // Validate outputId is child of the component
251         final Optional<OutputDefinition> optionalOutput = component.getOutputs().stream().
252             // filter by ID
253                 filter(output -> output.getUniqueId().equals(outputId)).
254             // Get the output
255                 findAny();
256         if (!optionalOutput.isPresent()) {
257             throw new ByActionStatusComponentException(ActionStatus.OUTPUT_IS_NOT_CHILD_OF_COMPONENT, outputId, componentId);
258         }
259         final OutputDefinition outputForDelete = optionalOutput.get();
260         // Lock component
261         lockComponent(componentId, component, "deleteOutput");
262         // Delete output operations
263         boolean failed = false;
264         try {
265             final StorageOperationStatus status = toscaOperationFacade.deleteOutputOfResource(component, outputForDelete.getName());
266             if (status != StorageOperationStatus.OK) {
267                 log.debug("Component id: {} delete output id: {} failed", componentId, outputId);
268                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(status), component.getName());
269             }
270             final StorageOperationStatus storageOperationStatus = attributeDeclarationOrchestrator
271                 .unDeclareAttributesAsOutputs(component, outputForDelete);
272             if (storageOperationStatus != StorageOperationStatus.OK) {
273                 log.debug("Component id: {} update attributes declared as output for outputId: {} failed", componentId, outputId);
274                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(storageOperationStatus), component.getName());
275             }
276             return outputForDelete;
277         } catch (final ComponentException e) {
278             failed = true;
279             throw e;
280         } finally {
281             unlockComponent(failed, component);
282         }
283     }
284
285     public Either<List<OutputDefinition>, ResponseFormat> createOutputsInGraph(final Map<String, OutputDefinition> outputs,
286                                                                                final Component component,
287                                                                                final String userId) {
288
289         final List<OutputDefinition> result = new ArrayList<>();
290         for (final Map.Entry<String, OutputDefinition> outputDefinition : outputs.entrySet()) {
291             final var outputDefinitionValue = outputDefinition.getValue();
292             outputDefinitionValue.setName(outputDefinition.getKey());
293
294             final String value = outputDefinitionValue.getValue();
295             if (value != null) {
296                 final List<String> getAttribute = (List<String>) ImportUtils.loadYamlAsStrictMap(value)
297                     .get(ToscaGetFunctionType.GET_ATTRIBUTE.getFunctionName());
298                 if (getAttribute.size() == 2) {
299                     final var optionalComponentInstance = component.getComponentInstanceByName(getAttribute.get(0));
300                     if (optionalComponentInstance.isPresent()) {
301                         final var createdOutputs
302                             = createOutputs(component.getUniqueId(), userId, getAttribute.get(1), optionalComponentInstance.get());
303                         if (createdOutputs.isRight()) {
304                             return Either.right((createdOutputs.right().value()));
305                         }
306                         result.addAll(createdOutputs.left().value());
307                     } else {
308                         // From SELF
309                         outputDefinitionValue.setInstanceUniqueId(component.getUniqueId());
310                     }
311                 }
312             }
313         }
314         return Either.left(result);
315
316     }
317
318     private Either<List<OutputDefinition>, ResponseFormat> createOutputs(final String componentUniqueId, final String userId,
319                                                                          final String attributeName,
320                                                                          final ComponentInstance componentInstance) {
321         // From Instance
322         final List<OutputDefinition> result = new ArrayList<>();
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                 final var createdOutputs = createMultipleOutputs(userId, componentUniqueId, ComponentTypeEnum.SERVICE,
332                     componentInstOutputsMap, true, false);
333                 if (createdOutputs.isRight()) {
334                     return Either.right((createdOutputs.right().value()));
335                 }
336                 result.addAll(createdOutputs.left().value());
337             }
338         }
339         final List<PropertyDefinition> componentInstanceProperties = componentInstance.getProperties();
340         if (CollectionUtils.isNotEmpty(componentInstanceProperties)) {
341             final Optional<PropertyDefinition> componentInstancePropertyOptional = componentInstanceProperties.stream()
342                 .filter(prop -> prop.getName().equals(attributeName)).findFirst();
343             if (componentInstancePropertyOptional.isPresent()) {
344                 PropertyDefinition propertyDefinition = componentInstancePropertyOptional.get();
345                 final ComponentInstOutputsMap componentInstOutputsMap = new ComponentInstOutputsMap();
346                 ComponentInstanceAttribOutput attribute = getComponentInstanceAttribOutput(propertyDefinition);
347                 componentInstOutputsMap.setComponentInstanceAttributes(Collections.singletonMap(componentInstance.getUniqueId(),
348                     Collections.singletonList(new ComponentInstanceAttribOutput(attribute))));
349                 return createMultipleOutputs(userId, componentUniqueId, ComponentTypeEnum.SERVICE, componentInstOutputsMap, true, false);
350             }
351         }
352         return Either.left(result);
353     }
354
355     @NotNull
356     private ComponentInstanceAttribOutput getComponentInstanceAttribOutput(PropertyDefinition propertyDefinition) {
357         ComponentInstanceAttribOutput attribute = new ComponentInstanceAttribOutput();
358         attribute.setParentUniqueId(propertyDefinition.getParentUniqueId());
359         attribute.setName(propertyDefinition.getName());
360         attribute.setOwnerId(propertyDefinition.getOwnerId());
361         attribute.setType(propertyDefinition.getType());
362         attribute.setSchema(propertyDefinition.getSchema());
363         attribute.setUniqueId(propertyDefinition.getUniqueId());
364         return attribute;
365     }
366
367 }