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