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