Revert "Fix 'Wrong Inputs creation on (Add Service)'"
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / InputsBusinessLogic.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. 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  * Modifications copyright (c) 2019 Nokia
20  * ================================================================================
21  */
22 package org.openecomp.sdc.be.components.impl;
23
24 import fj.data.Either;
25 import java.util.ArrayList;
26 import java.util.Arrays;
27 import java.util.Collections;
28 import java.util.HashMap;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.Objects;
32 import java.util.Optional;
33 import java.util.stream.Collectors;
34 import org.apache.commons.collections.CollectionUtils;
35 import org.apache.commons.collections4.ListUtils;
36 import org.apache.commons.collections4.MapUtils;
37 import org.apache.commons.lang3.BooleanUtils;
38 import org.apache.commons.lang3.StringUtils;
39 import org.apache.commons.lang3.builder.ReflectionToStringBuilder;
40 import org.openecomp.sdc.be.components.impl.exceptions.ByActionStatusComponentException;
41 import org.openecomp.sdc.be.components.impl.exceptions.ByResponseFormatComponentException;
42 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
43 import org.openecomp.sdc.be.components.property.PropertyDeclarationOrchestrator;
44 import org.openecomp.sdc.be.components.validation.ComponentValidations;
45 import org.openecomp.sdc.be.config.BeEcompErrorManager;
46 import org.openecomp.sdc.be.dao.api.ActionStatus;
47 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
48 import org.openecomp.sdc.be.dao.utils.MapUtil;
49 import org.openecomp.sdc.be.datamodel.utils.PropertyValueConstraintValidationUtil;
50 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
51 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
52 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
53 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
54 import org.openecomp.sdc.be.datatypes.tosca.ToscaDataDefinition;
55 import org.openecomp.sdc.be.model.Component;
56 import org.openecomp.sdc.be.model.ComponentInstInputsMap;
57 import org.openecomp.sdc.be.model.ComponentInstListInput;
58 import org.openecomp.sdc.be.model.ComponentInstance;
59 import org.openecomp.sdc.be.model.ComponentInstanceInput;
60 import org.openecomp.sdc.be.model.ComponentInstancePropInput;
61 import org.openecomp.sdc.be.model.ComponentInstanceProperty;
62 import org.openecomp.sdc.be.model.ComponentParametersView;
63 import org.openecomp.sdc.be.model.DataTypeDefinition;
64 import org.openecomp.sdc.be.model.InputDefinition;
65 import org.openecomp.sdc.be.model.PropertyDefinition;
66 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.ArtifactsOperations;
67 import org.openecomp.sdc.be.model.jsonjanusgraph.operations.InterfaceOperation;
68 import org.openecomp.sdc.be.model.operations.api.IElementOperation;
69 import org.openecomp.sdc.be.model.operations.api.IGroupInstanceOperation;
70 import org.openecomp.sdc.be.model.operations.api.IGroupOperation;
71 import org.openecomp.sdc.be.model.operations.api.IGroupTypeOperation;
72 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
73 import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
74 import org.openecomp.sdc.be.model.operations.impl.InterfaceLifecycleOperation;
75 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
76 import org.openecomp.sdc.be.model.operations.utils.ComponentValidationUtils;
77 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
78 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
79 import org.openecomp.sdc.be.resources.data.EntryData;
80 import org.openecomp.sdc.common.log.elements.LoggerSupportability;
81 import org.openecomp.sdc.common.log.enums.LoggerSupportabilityActions;
82 import org.openecomp.sdc.common.log.enums.StatusCode;
83 import org.openecomp.sdc.common.log.wrappers.Logger;
84 import org.openecomp.sdc.exception.ResponseFormat;
85 import org.springframework.beans.factory.annotation.Autowired;
86
87 @org.springframework.stereotype.Component("inputsBusinessLogic")
88 public class InputsBusinessLogic extends BaseBusinessLogic {
89
90     private static final String CREATE_INPUT = "CreateInput";
91     private static final String UPDATE_INPUT = "UpdateInput";
92     private static final Logger log = Logger.getLogger(InputsBusinessLogic.class);
93     private static final String FAILED_TO_FOUND_COMPONENT_ERROR = "Failed to found component {}, error: {}";
94     private static final String FAILED_TO_FOUND_INPUT_UNDER_COMPONENT_ERROR = "Failed to found input {} under component {}, error: {}";
95     private static final String GOING_TO_EXECUTE_ROLLBACK_ON_CREATE_GROUP = "Going to execute rollback on create group.";
96     private static final String GOING_TO_EXECUTE_COMMIT_ON_CREATE_GROUP = "Going to execute commit on create group.";
97     private static final String GOING_TO_EXECUTE_ROLLBACK_ON_UPDATE_INPUT = "Going to execute rollback on update input.";
98     private static final String GOING_TO_EXECUTE_COMMIT_ON_UPDATE_INPUT = "Going to execute commit on update input.";
99     private static final LoggerSupportability loggerSupportability = LoggerSupportability.getLogger(InputsBusinessLogic.class.getName());
100     private final PropertyDeclarationOrchestrator propertyDeclarationOrchestrator;
101     private final ComponentInstanceBusinessLogic componentInstanceBusinessLogic;
102     private final DataTypeBusinessLogic dataTypeBusinessLogic;
103
104     @Autowired
105     public InputsBusinessLogic(IElementOperation elementDao, IGroupOperation groupOperation, IGroupInstanceOperation groupInstanceOperation,
106                                IGroupTypeOperation groupTypeOperation, InterfaceOperation interfaceOperation,
107                                InterfaceLifecycleOperation interfaceLifecycleTypeOperation,
108                                PropertyDeclarationOrchestrator propertyDeclarationOrchestrator,
109                                ComponentInstanceBusinessLogic componentInstanceBusinessLogic, DataTypeBusinessLogic dataTypeBusinessLogic,
110                                ArtifactsOperations artifactToscaOperation) {
111         super(elementDao, groupOperation, groupInstanceOperation, groupTypeOperation, interfaceOperation, interfaceLifecycleTypeOperation,
112             artifactToscaOperation);
113         this.propertyDeclarationOrchestrator = propertyDeclarationOrchestrator;
114         this.componentInstanceBusinessLogic = componentInstanceBusinessLogic;
115         this.dataTypeBusinessLogic = dataTypeBusinessLogic;
116     }
117
118     /**
119      * associate inputs to a given component with paging
120      *
121      * @param userId
122      * @param componentId
123      * @return
124      */
125     public Either<List<InputDefinition>, ResponseFormat> getInputs(String userId, String componentId) {
126         validateUserExists(userId);
127         ComponentParametersView filters = new ComponentParametersView();
128         filters.disableAll();
129         filters.setIgnoreInputs(false);
130         Either<Component, StorageOperationStatus> getComponentEither = toscaOperationFacade
131             .getToscaElement(componentId, filters);
132         if (getComponentEither.isRight()) {
133             ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(getComponentEither.right().value());
134             log.debug(FAILED_TO_FOUND_COMPONENT_ERROR, componentId, actionStatus);
135             return Either.right(componentsUtils.getResponseFormat(actionStatus));
136         }
137         Component component = getComponentEither.left().value();
138         List<InputDefinition> inputs = component.getInputs();
139         return Either.left(inputs);
140     }
141
142     public Either<List<ComponentInstanceInput>, ResponseFormat> getComponentInstanceInputs(String userId, String componentId,
143                                                                                            String componentInstanceId) {
144         validateUserExists(userId);
145         ComponentParametersView filters = new ComponentParametersView();
146         filters.disableAll();
147         filters.setIgnoreInputs(false);
148         filters.setIgnoreComponentInstances(false);
149         filters.setIgnoreComponentInstancesInputs(false);
150         Either<Component, StorageOperationStatus> getComponentEither = toscaOperationFacade
151             .getToscaElement(componentId, filters);
152         if (getComponentEither.isRight()) {
153             ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(getComponentEither.right().value());
154             log.debug(FAILED_TO_FOUND_COMPONENT_ERROR, componentId, actionStatus);
155             return Either.right(componentsUtils.getResponseFormat(actionStatus));
156         }
157         Component component = getComponentEither.left().value();
158         if (!ComponentValidations.validateComponentInstanceExist(component, componentInstanceId)) {
159             ActionStatus actionStatus = ActionStatus.COMPONENT_INSTANCE_NOT_FOUND;
160             log.debug("Failed to found component instance inputs {}, error: {}", componentInstanceId, actionStatus);
161             loggerSupportability.log(LoggerSupportabilityActions.CREATE_INPUTS, component.getComponentMetadataForSupportLog(), StatusCode.ERROR,
162                 "Failed to found component instance inputs componentInstanceId: {}", componentInstanceId);
163             return Either.right(componentsUtils.getResponseFormat(actionStatus));
164         }
165         Map<String, List<ComponentInstanceInput>> ciInputs = Optional.ofNullable(component.getComponentInstancesInputs())
166             .orElse(Collections.emptyMap());
167         // Set Constraints on Input
168         MapUtils.emptyIfNull(ciInputs).values()
169             .forEach(inputs -> ListUtils.emptyIfNull(inputs).forEach(input -> input.setConstraints(setInputConstraint(input))));
170         return Either.left(ciInputs.getOrDefault(componentInstanceId, Collections.emptyList()));
171     }
172
173     /**
174      * associate properties to a given component instance input
175      *
176      * @param instanceId
177      * @param userId
178      * @param inputId
179      * @return
180      */
181     public Either<List<ComponentInstanceProperty>, ResponseFormat> getComponentInstancePropertiesByInputId(String userId, String componentId,
182                                                                                                            String instanceId, String inputId) {
183         validateUserExists(userId);
184         String parentId = componentId;
185         Component component;
186         ComponentParametersView filters = new ComponentParametersView();
187         filters.disableAll();
188         filters.setIgnoreComponentInstances(false);
189         if (!instanceId.equals(inputId)) {
190             Either<Component, StorageOperationStatus> getComponentEither = toscaOperationFacade
191                 .getToscaElement(parentId, filters);
192             if (getComponentEither.isRight()) {
193                 ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(getComponentEither.right().value());
194                 log.debug(FAILED_TO_FOUND_COMPONENT_ERROR, parentId, actionStatus);
195                 return Either.right(componentsUtils.getResponseFormat(actionStatus));
196             }
197             component = getComponentEither.left().value();
198             Optional<ComponentInstance> ciOp = component.getComponentInstances().stream().filter(ci -> ci.getUniqueId().equals(instanceId)).findAny();
199             if (ciOp.isPresent()) {
200                 parentId = ciOp.get().getComponentUid();
201             }
202         }
203         filters.setIgnoreInputs(false);
204         filters.setIgnoreComponentInstancesProperties(false);
205         filters.setIgnoreComponentInstancesInputs(false);
206         filters.setIgnoreProperties(false);
207         Either<Component, StorageOperationStatus> getComponentEither = toscaOperationFacade
208             .getToscaElement(parentId, filters);
209         if (getComponentEither.isRight()) {
210             ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(getComponentEither.right().value());
211             log.debug(FAILED_TO_FOUND_COMPONENT_ERROR, parentId, actionStatus);
212             return Either.right(componentsUtils.getResponseFormat(actionStatus));
213         }
214         component = getComponentEither.left().value();
215         Optional<InputDefinition> op = component.getInputs().stream().filter(in -> in.getUniqueId().equals(inputId)).findFirst();
216         if (!op.isPresent()) {
217             ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(getComponentEither.right().value());
218             log.debug(FAILED_TO_FOUND_INPUT_UNDER_COMPONENT_ERROR, inputId, parentId, actionStatus);
219             return Either.right(componentsUtils.getResponseFormat(actionStatus));
220         }
221         return Either.left(componentInstanceBusinessLogic.getComponentInstancePropertiesByInputId(component, inputId));
222     }
223
224     private String updateInputObjectValue(InputDefinition currentInput, InputDefinition newInput, Map<String, DataTypeDefinition> dataTypes) {
225         String innerType = null;
226         String propertyType = currentInput.getType();
227         ToscaPropertyType type = ToscaPropertyType.isValidType(propertyType);
228         log.debug("The type of the property {} is {}", currentInput.getUniqueId(), propertyType);
229         if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
230             SchemaDefinition def = currentInput.getSchema();
231             if (def == null) {
232                 log.debug("Schema doesn't exists for property of type {}", type);
233                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(StorageOperationStatus.INVALID_VALUE));
234             }
235             PropertyDataDefinition propDef = def.getProperty();
236             if (propDef == null) {
237                 log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
238                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(StorageOperationStatus.INVALID_VALUE));
239             }
240             innerType = propDef.getType();
241         }
242         // Specific Update Logic
243         Either<Object, Boolean> isValid = propertyOperation
244             .validateAndUpdatePropertyValue(propertyType, newInput.getDefaultValue(), true, innerType, dataTypes);
245         String newValue = newInput.getDefaultValue();
246         if (isValid.isRight()) {
247             Boolean res = isValid.right().value();
248             if (Boolean.FALSE.equals(res)) {
249                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(
250                     DaoStatusConverter.convertJanusGraphStatusToStorageStatus(JanusGraphOperationStatus.ILLEGAL_ARGUMENT)));
251             }
252         } else {
253             Object object = isValid.left().value();
254             if (object != null) {
255                 newValue = object.toString();
256             }
257         }
258         return newValue;
259     }
260
261     private InputDefinition getInputFromInputsListById(List<InputDefinition> componentsOldInputs, InputDefinition input) {
262         return componentsOldInputs.stream().filter(in -> in.getUniqueId().equals(input.getUniqueId())).findFirst().orElse(null);
263     }
264
265     public Either<List<InputDefinition>, ResponseFormat> updateInputsValue(ComponentTypeEnum componentType, String componentId,
266                                                                            List<InputDefinition> inputs, String userId, boolean shouldLockComp) {
267         List<InputDefinition> returnInputs = new ArrayList<>();
268         Either<List<InputDefinition>, ResponseFormat> result = null;
269         Component component = null;
270         try {
271             validateUserExists(userId);
272             ComponentParametersView componentParametersView = new ComponentParametersView();
273             componentParametersView.disableAll();
274             componentParametersView.setIgnoreInputs(false);
275             componentParametersView.setIgnoreUsers(false);
276             componentParametersView.setIgnoreProperties(false);
277             componentParametersView.setIgnoreComponentInstancesProperties(false);
278             componentParametersView.setIgnoreComponentInstances(false);
279             component = validateComponentExists(componentId, componentType, componentParametersView);
280             if (shouldLockComp) {
281                 try {
282                     lockComponent(component, UPDATE_INPUT);
283                 } catch (ComponentException e) {
284                     log.error("Failed to lock component", e);
285                     result = Either.right(e.getResponseFormat());
286                     return result;
287                 }
288             }
289             //Validate value and Constraint of input
290             Either<Boolean, ResponseFormat> constraintValidatorResponse = validateInputValueConstraint(inputs, component.getModel());
291             if (constraintValidatorResponse.isRight()) {
292                 log.error("Failed validation value and constraint of property: {}", constraintValidatorResponse.right().value());
293                 return Either.right(constraintValidatorResponse.right().value());
294             }
295             validateCanWorkOnComponent(component, userId);
296             Map<String, DataTypeDefinition> dataTypes;
297             dataTypes = componentsUtils.getAllDataTypes(applicationDataTypeCache, component.getModel());
298             List<InputDefinition> componentsOldInputs = Optional.ofNullable(component.getInputs()).orElse(Collections.emptyList());
299             for (InputDefinition newInput : inputs) {
300                 InputDefinition currInput = getInputFromInputsListById(componentsOldInputs, newInput);
301                 if (currInput == null) {
302                     ActionStatus actionStatus = ActionStatus.COMPONENT_NOT_FOUND;
303                     log.debug("Failed to found newInput {} under component {}, error: {}", newInput.getUniqueId(), componentId, actionStatus);
304                     result = Either.right(componentsUtils.getResponseFormat(actionStatus));
305                     return result;
306                 }
307                 String updateInputObjectValue = updateInputObjectValue(currInput, newInput, dataTypes);
308                 currInput.setDefaultValue(updateInputObjectValue);
309                 currInput.setOwnerId(userId);
310                 currInput.setMetadata(newInput.getMetadata());
311                 if (newInput.isRequired() != null) {
312                     currInput.setRequired(newInput.isRequired());
313                 }
314                 Either<InputDefinition, StorageOperationStatus> status = toscaOperationFacade.updateInputOfComponent(component, currInput);
315                 if (status.isRight()) {
316                     ActionStatus actionStatus = componentsUtils.convertFromStorageResponseForResourceInstanceProperty(status.right().value());
317                     result = Either.right(componentsUtils.getResponseFormat(actionStatus, ""));
318                     return result;
319                 } else {
320                     returnInputs.add(status.left().value());
321                 }
322             }
323             result = Either.left(returnInputs);
324         } catch (ComponentException e) {
325             log.debug(GOING_TO_EXECUTE_ROLLBACK_ON_UPDATE_INPUT);
326             unlockRollbackWithException(component, e);
327         } catch (Exception e) {
328             log.debug(GOING_TO_EXECUTE_ROLLBACK_ON_UPDATE_INPUT);
329             unlockRollbackWithException(component, new ByActionStatusComponentException(ActionStatus.GENERAL_ERROR));
330         }
331         log.debug(GOING_TO_EXECUTE_COMMIT_ON_UPDATE_INPUT);
332         unlockWithCommit(component);
333         return result;
334     }
335
336     private Either<Boolean, ResponseFormat> validateInputValueConstraint(List<InputDefinition> inputs, final String model) {
337         PropertyValueConstraintValidationUtil propertyValueConstraintValidationUtil = new PropertyValueConstraintValidationUtil();
338         List<InputDefinition> inputDefinitions = new ArrayList<>();
339         for (InputDefinition inputDefinition : inputs) {
340             InputDefinition inputDef = new InputDefinition();
341             inputDefinition.setDefaultValue(inputDefinition.getDefaultValue());
342             inputDefinition.setInputPath(inputDefinition.getSubPropertyInputPath());
343             inputDefinition.setType(inputDefinition.getType());
344             if (Objects.nonNull(inputDefinition.getParentPropertyType())) {
345                 ComponentInstanceProperty propertyDefinition = new ComponentInstanceProperty();
346                 propertyDefinition.setType(inputDefinition.getParentPropertyType());
347                 inputDefinition.setProperties(Collections.singletonList(propertyDefinition));
348             }
349             inputDefinitions.add(inputDef);
350         }
351         return propertyValueConstraintValidationUtil.validatePropertyConstraints(inputDefinitions, applicationDataTypeCache, model);
352     }
353
354     public Either<List<ComponentInstanceInput>, ResponseFormat> getInputsForComponentInput(String userId, String componentId, String inputId) {
355         validateUserExists(userId);
356         Component component = null;
357         ComponentParametersView filters = new ComponentParametersView();
358         filters.disableAll();
359         filters.setIgnoreComponentInstances(false);
360         filters.setIgnoreInputs(false);
361         filters.setIgnoreComponentInstancesInputs(false);
362         filters.setIgnoreProperties(false);
363         Either<Component, StorageOperationStatus> getComponentEither = toscaOperationFacade
364             .getToscaElement(componentId, filters);
365         if (getComponentEither.isRight()) {
366             ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(getComponentEither.right().value());
367             log.debug(FAILED_TO_FOUND_COMPONENT_ERROR, componentId, actionStatus);
368             return Either.right(componentsUtils.getResponseFormat(actionStatus));
369         }
370         component = getComponentEither.left().value();
371         Optional<InputDefinition> op = component.getInputs().stream().filter(in -> in.getUniqueId().equals(inputId)).findFirst();
372         if (!op.isPresent()) {
373             ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(getComponentEither.right().value());
374             log.debug(FAILED_TO_FOUND_INPUT_UNDER_COMPONENT_ERROR, inputId, componentId, actionStatus);
375             return Either.right(componentsUtils.getResponseFormat(actionStatus));
376         }
377         return Either.left(componentInstanceBusinessLogic.getComponentInstanceInputsByInputId(component, inputId));
378     }
379
380     @Override
381     public Either<List<InputDefinition>, ResponseFormat> declareProperties(String userId, String componentId, ComponentTypeEnum componentTypeEnum,
382                                                                            ComponentInstInputsMap componentInstInputsMap) {
383         return createMultipleInputs(userId, componentId, componentTypeEnum, componentInstInputsMap, true, false);
384     }
385
386     private Either<List<InputDefinition>, ResponseFormat> createMultipleInputs(String userId, String componentId, ComponentTypeEnum componentType,
387                                                                                ComponentInstInputsMap componentInstInputsMapUi,
388                                                                                boolean shouldLockComp,
389                                                                                boolean inTransaction) {
390         Either<List<InputDefinition>, ResponseFormat> result = null;
391         Component component = null;
392         try {
393             validateUserExists(userId);
394             component = getAndValidateComponentForCreate(userId, componentId, componentType, shouldLockComp);
395             result = propertyDeclarationOrchestrator.declarePropertiesToInputs(component, componentInstInputsMapUi).left()
396                 .bind(inputsToCreate -> prepareInputsForCreation(userId, componentId, inputsToCreate)).right()
397                 .map(componentsUtils::getResponseFormat);
398             return result;
399         } catch (ByResponseFormatComponentException e) {
400             log.error("#createMultipleInputs: Exception thrown: ", e);
401             result = Either.right(e.getResponseFormat());
402             return result;
403         } finally {
404             if (!inTransaction) {
405                 if (result == null || result.isRight()) {
406                     log.debug(GOING_TO_EXECUTE_ROLLBACK_ON_CREATE_GROUP);
407                     janusGraphDao.rollback();
408                 } else {
409                     log.debug(GOING_TO_EXECUTE_COMMIT_ON_CREATE_GROUP);
410                     janusGraphDao.commit();
411                 }
412             }
413             // unlock resource
414             if (shouldLockComp && component != null) {
415                 graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
416             }
417         }
418     }
419
420     /**
421      * Creates a list input with a data type which has properties specified.
422      *
423      * @param userId             User ID
424      * @param componentId        Component ID
425      * @param componentType      Component type
426      * @param componentListInput Properties to be declared and input to be created
427      * @param shouldLockComp     true if the component should be locked
428      * @param inTransaction      true if already in transaction
429      */
430     public Either<List<InputDefinition>, ResponseFormat> createListInput(String userId, String componentId, ComponentTypeEnum componentType,
431                                                                          ComponentInstListInput componentListInput, boolean shouldLockComp,
432                                                                          boolean inTransaction) {
433         Either<List<InputDefinition>, ResponseFormat> result = null;
434         Component component = null;
435         log.trace("#createListInput: enter");
436         try {
437             /* check if user exists */
438             validateUserExists(userId);
439
440             component = getAndValidateComponentForCreate(userId, componentId, componentType, shouldLockComp);
441
442             InputDefinition listInput = componentListInput.getListInput();
443             DataTypeDefinition dataType =
444                 prepareDataTypeForListInput(componentListInput.getComponentInstInputsMap(), listInput);
445             Map<String, DataTypeDefinition> dataTypesMap = new HashMap<>();
446             dataTypesMap.put(dataType.getName(), dataType);
447             if (log.isDebugEnabled()) {
448                 log.debug("#createListInput: dataTypesMap={}", ReflectionToStringBuilder.toString(dataTypesMap));
449             }
450
451             Either<List<DataTypeDefinition>, StorageOperationStatus> dataTypeResult =
452                 toscaOperationFacade.addDataTypesToComponent(dataTypesMap, componentId);
453             if (dataTypeResult.isRight()) {
454                 log.debug("#createListInput: DataType creation failed.");
455                 throw new ByResponseFormatComponentException(componentsUtils.getResponseFormat(dataTypeResult.right().value()));
456             }
457
458             // create list input
459             listInput.setUniqueId(UniqueIdBuilder.buildPropertyUniqueId(componentId, listInput.getName()));
460             listInput.setInstanceUniqueId(
461                 propertyDeclarationOrchestrator.getPropOwnerId(componentListInput.getComponentInstInputsMap()));
462             listInput.setIsDeclaredListInput(true);
463             Map<String, InputDefinition> listInputMap = new HashMap<>();
464             listInputMap.put(listInput.getName(), listInput);
465             result = createListInputsInGraph(listInputMap, dataTypesMap, component);
466             if (result.isRight()) {
467                 log.debug("#createListInput: createListInputsInGraph failed.");
468                 throw new ByResponseFormatComponentException(result.right().value());
469             }
470
471             // update properties
472             result = propertyDeclarationOrchestrator
473                 .declarePropertiesToListInput(component, componentListInput.getComponentInstInputsMap(), listInput)
474                 .right().map(err -> componentsUtils.getResponseFormat(err))
475                 .left().map(Arrays::asList);
476
477             log.trace("#createListInput: leave");
478
479             return result;
480
481         } catch (ByResponseFormatComponentException e) {
482             log.error("#createListInput: Exception thrown", e);
483             result = Either.right(e.getResponseFormat());
484             return result;
485         } finally {
486
487             if (!inTransaction) {
488                 if (result == null || result.isRight()) {
489                     log.debug(GOING_TO_EXECUTE_ROLLBACK_ON_CREATE_GROUP);
490                     janusGraphDao.rollback();
491                 } else {
492                     log.debug(GOING_TO_EXECUTE_COMMIT_ON_CREATE_GROUP);
493                     janusGraphDao.commit();
494                 }
495             }
496             // unlock resource
497             if (shouldLockComp && component != null) {
498                 graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
499             }
500         }
501     }
502
503     private ComponentParametersView getBaseComponentParametersView() {
504         ComponentParametersView componentParametersView = new ComponentParametersView();
505         componentParametersView.disableAll();
506         componentParametersView.setIgnoreInputs(false);
507         componentParametersView.setIgnoreComponentInstances(false);
508         componentParametersView.setIgnoreComponentInstancesInputs(false);
509         componentParametersView.setIgnoreComponentInstancesProperties(false);
510         componentParametersView.setIgnorePolicies(false);
511         componentParametersView.setIgnoreGroups(false);
512         componentParametersView.setIgnoreUsers(false);
513         return componentParametersView;
514     }
515
516     private Component getAndValidateComponentForCreate(
517         String userId, String componentId, ComponentTypeEnum componentType, boolean shouldLockComp
518     ) {
519         ComponentParametersView componentParametersView = getBaseComponentParametersView();
520         Component component = validateComponentExists(componentId, componentType, componentParametersView);
521         if (shouldLockComp) {
522             // lock the component
523             lockComponent(component, CREATE_INPUT);
524         }
525         validateCanWorkOnComponent(component, userId);
526         return component;
527     }
528
529     private DataTypeDefinition prepareDataTypeForListInput(ComponentInstInputsMap inputsMap, InputDefinition input) {
530         // Confirm if type is list
531         if (StringUtils.isEmpty(input.getType()) || !input.getType().equals(ToscaPropertyType.LIST.getType())) {
532             log.debug("#prepareDataTypeForListInput: Type of input is not list.");
533             throw new ByResponseFormatComponentException(componentsUtils.getResponseFormat(ActionStatus.INVALID_PROPERTY_TYPE));
534         }
535
536         // Confirm schema type is not empty
537         String desiredTypeName = input.getSchemaType();
538         if (StringUtils.isEmpty(desiredTypeName)) {
539             log.debug("#prepareDataTypeForListInput: Schema type of list input is empty.");
540             throw new ByResponseFormatComponentException(componentsUtils.getResponseFormat(ActionStatus.INVALID_PROPERTY_INNER_TYPE));
541         }
542
543         DataTypeDefinition dataType = new DataTypeDefinition();
544         List<ComponentInstancePropInput> propInputs = inputsMap.resolvePropertiesToDeclare().getRight();
545         dataType.setName(desiredTypeName);
546         dataType.setDerivedFromName(ToscaPropertyType.ROOT.getType());
547         // Copy properties from inputsMap
548         dataType.setProperties(propInputs.stream().map(PropertyDefinition::new).collect(Collectors.toList()));
549         return dataType;
550     }
551
552     private Either<List<InputDefinition>, StorageOperationStatus> prepareInputsForCreation(String userId, String cmptId,
553                                                                                            List<InputDefinition> inputsToCreate) {
554         Map<String, InputDefinition> inputsToPersist = MapUtil.toMap(inputsToCreate, InputDefinition::getName);
555         assignOwnerIdToInputs(userId, inputsToPersist);
556         inputsToPersist.values()
557             .forEach(input -> input.setConstraints(componentInstanceBusinessLogic.setInputConstraint(input)));
558
559         return toscaOperationFacade.addInputsToComponent(inputsToPersist, cmptId)
560             .left()
561             .map(persistedInputs -> inputsToCreate);
562     }
563
564     private void assignOwnerIdToInputs(String userId, Map<String, InputDefinition> inputsToCreate) {
565         inputsToCreate.values().forEach(inputDefinition -> inputDefinition.setOwnerId(userId));
566     }
567
568     public Either<List<InputDefinition>, ResponseFormat> createInputsInGraph(Map<String, InputDefinition> inputs,
569                                                                              Component component) {
570
571         List<InputDefinition> resourceProperties = component.getInputs();
572
573         final Map<String, DataTypeDefinition> dataTypes = componentsUtils.getAllDataTypes(applicationDataTypeCache, component.getModel());
574
575         for (Map.Entry<String, InputDefinition> inputDefinition : inputs.entrySet()) {
576             String inputName = inputDefinition.getKey();
577             inputDefinition.getValue().setName(inputName);
578
579             Either<InputDefinition, ResponseFormat> preparedInputEither = prepareAndValidateInputBeforeCreate(inputDefinition.getValue(), dataTypes);
580             if (preparedInputEither.isRight()) {
581                 return Either.right(preparedInputEither.right().value());
582             }
583
584         }
585         if (resourceProperties != null) {
586             Map<String, InputDefinition> generatedInputs = resourceProperties.stream()
587                 .collect(Collectors.toMap(PropertyDataDefinition::getName, i -> i));
588             Either<Map<String, InputDefinition>, String> mergeEither = ToscaDataDefinition.mergeDataMaps(generatedInputs, inputs);
589             if (mergeEither.isRight()) {
590                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_ALREADY_EXIST, mergeEither.right().value()));
591             }
592             inputs = mergeEither.left().value();
593         }
594
595         Either<List<InputDefinition>, StorageOperationStatus> associateInputsEither = toscaOperationFacade
596             .createAndAssociateInputs(inputs, component.getUniqueId());
597         if (associateInputsEither.isRight()) {
598             log.debug("Failed to create inputs under component {}. Status is {}", component.getUniqueId(), associateInputsEither.right().value());
599
600             return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(associateInputsEither.right().value())));
601         }
602         return Either.left(associateInputsEither.left().value());
603     }
604
605     private Either<List<InputDefinition>, ResponseFormat> createListInputsInGraph(Map<String, InputDefinition> inputs,
606                                                                                   Map<String, DataTypeDefinition> privateDataTypes,
607                                                                                   Component component) {
608
609         log.trace("#createListInputsInGraph: enter");
610
611         Map<String, DataTypeDefinition> dataTypes = componentsUtils.getAllDataTypes(applicationDataTypeCache, component.getModel());
612         dataTypes.putAll(privateDataTypes);
613
614         for (Map.Entry<String, InputDefinition> inputDefinition : inputs.entrySet()) {
615             String inputName = inputDefinition.getKey();
616             inputDefinition.getValue().setName(inputName);
617
618             Either<InputDefinition, ResponseFormat> preparedInputEither =
619                 prepareAndValidateInputBeforeCreate(inputDefinition.getValue(), dataTypes);
620             if (preparedInputEither.isRight()) {
621                 return Either.right(preparedInputEither.right().value());
622             }
623         }
624
625         Either<List<InputDefinition>, StorageOperationStatus> addInputsEither = toscaOperationFacade
626             .addInputsToComponent(inputs, component.getUniqueId());
627         if (addInputsEither.isRight()) {
628             log.debug("#createListInputsInGraph: Failed to create inputs under component {}. Status is {}",
629                 component.getUniqueId(), addInputsEither.right().value());
630             return Either.right(componentsUtils.getResponseFormat(
631                 componentsUtils.convertFromStorageResponse(addInputsEither.right().value())));
632         }
633         log.trace("#createListInputsInGraph: leave");
634         return Either.left(addInputsEither.left().value());
635     }
636
637     /**
638      * Delete input from service
639      *
640      * @param componentId
641      * @param userId
642      * @param inputId
643      * @return
644      */
645     public InputDefinition deleteInput(String componentId, String userId, String inputId) {
646         Either<InputDefinition, ResponseFormat> deleteEither = null;
647         if (log.isDebugEnabled()) {
648             log.debug("Going to delete input id: {}", inputId);
649         }
650         validateUserExists(userId);
651         ComponentParametersView componentParametersView = getBaseComponentParametersView();
652         componentParametersView.setIgnoreInterfaces(false);
653         componentParametersView.setIgnoreDataType(false);
654         componentParametersView.setIgnoreProperties(false);
655         Either<Component, StorageOperationStatus> componentEither = toscaOperationFacade
656             .getToscaElement(componentId, componentParametersView);
657         if (componentEither.isRight()) {
658             throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(componentEither.right().value()));
659         }
660         Component component = componentEither.left().value();
661         // Validate inputId is child of the component
662         Optional<InputDefinition> optionalInput = component.getInputs().stream().
663             // filter by ID
664                 filter(input -> input.getUniqueId().equals(inputId)).
665             // Get the input
666                 findAny();
667         if (!optionalInput.isPresent()) {
668             throw new ByActionStatusComponentException(ActionStatus.INPUT_IS_NOT_CHILD_OF_COMPONENT, inputId, componentId);
669         }
670         InputDefinition inputForDelete = optionalInput.get();
671         // Lock component
672         lockComponent(componentId, component, "deleteInput");
673         // Delete input operations
674         boolean failed = false;
675         try {
676             StorageOperationStatus status = toscaOperationFacade.deleteInputOfResource(component, inputForDelete.getName());
677             if (status != StorageOperationStatus.OK) {
678                 log.debug("Component id: {} delete input id: {} failed", componentId, inputId);
679                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(status), component.getName());
680             }
681             if (BooleanUtils.isTrue(inputForDelete.getIsDeclaredListInput())) {
682                 deleteEither = deleteListInput(componentId, inputId, component, inputForDelete, status);
683                 if (deleteEither.isRight()) {
684                     throw new ByResponseFormatComponentException(deleteEither.right().value());
685                 }
686                 return deleteEither.left().value();
687             }
688             StorageOperationStatus storageOperationStatus = propertyDeclarationOrchestrator.unDeclarePropertiesAsInputs(component, inputForDelete);
689             if (storageOperationStatus != StorageOperationStatus.OK) {
690                 log.debug("Component id: {} update properties declared as input for input id: {} failed", componentId, inputId);
691                 throw new ByActionStatusComponentException(componentsUtils.convertFromStorageResponse(storageOperationStatus), component.getName());
692             }
693             return inputForDelete;
694         } catch (ComponentException e) {
695             failed = true;
696             throw e;
697         } finally {
698             unlockComponent(failed, component);
699         }
700     }
701
702     private Either<InputDefinition, ResponseFormat> deleteListInput(String componentId, String inputId,
703                                                                     Component component, InputDefinition inputForDelete,
704                                                                     StorageOperationStatus status) {
705         // the input is created by 'Declare List'.
706         // need to 1. undeclare properties, 2. delete input, 3. delete private data type
707         StorageOperationStatus storageOperationStatus = propertyDeclarationOrchestrator.unDeclarePropertiesAsListInputs(component, inputForDelete);
708         if (storageOperationStatus != StorageOperationStatus.OK) {
709             log.debug("Component id: {} update properties declared as input for input id: {} failed", componentId, inputId);
710             return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status), component.getName()));
711         }
712         Either<DataTypeDefinition, StorageOperationStatus> deleteResult = dataTypeBusinessLogic
713             .deletePrivateDataType(component, inputForDelete.getSchemaType());
714         if (deleteResult.isRight()) {
715             log.debug("Component id: {} delete datatype name: {} failed", componentId, inputForDelete.getSchemaType());
716             return Either.right(
717                 componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(deleteResult.right().value()), component.getName()));
718         }
719         log.trace("deleteInput: deletePrivateDataType (OK)");
720         return Either.left(inputForDelete);
721     }
722
723     private Either<InputDefinition, ResponseFormat> prepareAndValidateInputBeforeCreate(InputDefinition newInputDefinition,
724                                                                                         Map<String, DataTypeDefinition> dataTypes) {
725         // validate input default values
726         Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newInputDefinition, dataTypes);
727         if (defaultValuesValidation.isRight()) {
728             return Either.right(defaultValuesValidation.right().value());
729         }
730         // convert property
731         ToscaPropertyType type = getType(newInputDefinition.getType());
732         if (type != null) {
733             PropertyValueConverter converter = type.getConverter();
734             // get inner type
735             SchemaDefinition schema = newInputDefinition.getSchema();
736             String innerType = null;
737             if (schema != null) {
738                 PropertyDataDefinition prop = schema.getProperty();
739                 if (prop != null) {
740                     innerType = prop.getType();
741                 }
742             }
743             String convertedValue;
744             if (newInputDefinition.getDefaultValue() != null) {
745                 convertedValue = converter.convert(newInputDefinition.getDefaultValue(), innerType, dataTypes);
746                 newInputDefinition.setDefaultValue(convertedValue);
747             }
748         }
749         return Either.left(newInputDefinition);
750     }
751
752     public Either<InputDefinition, ResponseFormat> getInputsAndPropertiesForComponentInput(String userId, String componentId, String inputId,
753                                                                                            boolean inTransaction) {
754         Either<InputDefinition, ResponseFormat> result = null;
755         try {
756             validateUserExists(userId);
757             ComponentParametersView filters = new ComponentParametersView();
758             filters.disableAll();
759             filters.setIgnoreComponentInstances(false);
760             filters.setIgnoreInputs(false);
761             filters.setIgnoreComponentInstancesInputs(false);
762             filters.setIgnoreComponentInstancesProperties(false);
763             filters.setIgnoreProperties(false);
764             Either<Component, StorageOperationStatus> getComponentEither = toscaOperationFacade
765                 .getToscaElement(componentId, filters);
766             if (getComponentEither.isRight()) {
767                 ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(getComponentEither.right().value());
768                 log.debug(FAILED_TO_FOUND_COMPONENT_ERROR, componentId, actionStatus);
769                 return Either.right(componentsUtils.getResponseFormat(actionStatus));
770             }
771             Component component = getComponentEither.left().value();
772             Optional<InputDefinition> op = component.getInputs().stream().filter(in -> in.getUniqueId().equals(inputId)).findFirst();
773             if (!op.isPresent()) {
774                 ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(getComponentEither.right().value());
775                 log.debug(FAILED_TO_FOUND_INPUT_UNDER_COMPONENT_ERROR, inputId, componentId, actionStatus);
776                 return Either.right(componentsUtils.getResponseFormat(actionStatus));
777             }
778             InputDefinition resObj = op.get();
779             List<ComponentInstanceInput> inputCIInput = componentInstanceBusinessLogic.getComponentInstanceInputsByInputId(component, inputId);
780             resObj.setInputs(inputCIInput);
781             List<ComponentInstanceProperty> inputProps = componentInstanceBusinessLogic.getComponentInstancePropertiesByInputId(component, inputId);
782             resObj.setProperties(inputProps);
783             result = Either.left(resObj);
784             return result;
785         } finally {
786             if (!inTransaction) {
787                 if (result == null || result.isRight()) {
788                     log.debug(GOING_TO_EXECUTE_ROLLBACK_ON_CREATE_GROUP);
789                     janusGraphDao.rollback();
790                 } else {
791                     log.debug(GOING_TO_EXECUTE_COMMIT_ON_CREATE_GROUP);
792                     janusGraphDao.commit();
793                 }
794             }
795         }
796     }
797
798     public Either<EntryData<String, InputDefinition>, ResponseFormat> addInputToComponent(String componentId, String inputName,
799                                                                                           InputDefinition newInputDefinition, String userId) {
800         Either<EntryData<String, InputDefinition>, ResponseFormat> result = null;
801         validateUserExists(userId);
802         Either<Component, StorageOperationStatus> serviceElement = toscaOperationFacade.getToscaElement(componentId);
803         if (serviceElement.isRight()) {
804             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESOURCE_NOT_FOUND, ""));
805             return result;
806         }
807         Component component = serviceElement.left().value();
808         NodeTypeEnum nodeType = component.getComponentType().getNodeType();
809         StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, nodeType);
810         if (!lockResult.equals(StorageOperationStatus.OK)) {
811             BeEcompErrorManager.getInstance().logBeFailedLockObjectError(CREATE_INPUT, nodeType.name().toLowerCase(), componentId);
812             log.info("Failed to lock component {}. Error - {}", componentId, lockResult);
813             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
814             return result;
815         }
816         try {
817             if (!ComponentValidationUtils.canWorkOnComponent(component, userId)) {
818                 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.RESTRICTED_OPERATION));
819                 return result;
820             }
821             List<InputDefinition> inputs = component.getInputs();
822             if (CollectionUtils.isEmpty(inputs)) {
823                 inputs = new ArrayList<>();
824             }
825             if (isInputExistInComponent(inputs, inputName)) {
826                 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.INPUT_ALREADY_EXIST, inputName));
827                 return result;
828             }
829             Map<String, DataTypeDefinition> allDataTypes = componentsUtils.getAllDataTypes(applicationDataTypeCache, component.getModel());
830             // validate input default values
831             Either<Boolean, ResponseFormat> defaultValuesValidation = validatePropertyDefaultValue(newInputDefinition, allDataTypes);
832             if (defaultValuesValidation.isRight()) {
833                 result = Either.right(defaultValuesValidation.right().value());
834                 return result;
835             }
836             // convert Input
837             ToscaPropertyType type = getType(newInputDefinition.getType());
838             if (type != null) {
839                 PropertyValueConverter converter = type.getConverter();
840                 // get inner type
841                 String innerType = null;
842                 SchemaDefinition schema = newInputDefinition.getSchema();
843                 if (schema != null) {
844                     PropertyDataDefinition prop = schema.getProperty();
845                     if (prop != null) {
846                         innerType = prop.getType();
847                     }
848                 }
849                 if (newInputDefinition.getDefaultValue() != null) {
850                     String convertedValue = converter.convert(newInputDefinition.getDefaultValue(), innerType, allDataTypes);
851                     newInputDefinition.setDefaultValue(convertedValue);
852                 }
853             }
854             newInputDefinition.setMappedToComponentProperty(false);
855             Either<InputDefinition, StorageOperationStatus> addInputEither = toscaOperationFacade
856                 .addInputToComponent(inputName, newInputDefinition, component);
857             if (addInputEither.isRight()) {
858                 log.info("Failed to add new input {}. Error - {}", componentId, addInputEither.right().value());
859                 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
860                 return result;
861             }
862             result = Either.left(new EntryData<>(inputName, newInputDefinition));
863             return result;
864         } finally {
865             commitOrRollback(result);
866             // unlock component
867             graphLockOperation.unlockComponent(componentId, nodeType);
868         }
869     }
870
871     private boolean isInputExistInComponent(List<InputDefinition> inputs, String inputName) {
872         return CollectionUtils.isNotEmpty(inputs) && inputs.stream().anyMatch(input -> input.getName().equals(inputName));
873     }
874 }