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