Sync Integ to Master
[sdc.git] / catalog-be / src / main / java / org / openecomp / sdc / be / components / impl / GroupBusinessLogic.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  */
20
21 package org.openecomp.sdc.be.components.impl;
22
23 import fj.data.Either;
24 import org.apache.commons.collections.CollectionUtils;
25 import org.apache.commons.collections.MapUtils;
26 import org.apache.commons.io.FilenameUtils;
27 import org.apache.commons.lang.StringUtils;
28 import org.apache.commons.lang3.math.NumberUtils;
29 import org.apache.commons.lang3.tuple.ImmutablePair;
30 import org.openecomp.sdc.be.components.impl.exceptions.ComponentException;
31 import org.openecomp.sdc.be.components.utils.Utils;
32 import org.openecomp.sdc.be.components.validation.AccessValidations;
33 import org.openecomp.sdc.be.components.validation.ComponentValidations;
34 import org.openecomp.sdc.be.config.BeEcompErrorManager;
35 import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
36 import org.openecomp.sdc.be.config.ConfigurationManager;
37 import org.openecomp.sdc.be.dao.api.ActionStatus;
38 import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
39 import org.openecomp.sdc.be.datatypes.elements.GroupDataDefinition;
40 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
41 import org.openecomp.sdc.be.datatypes.enums.ComponentTypeEnum;
42 import org.openecomp.sdc.be.info.ArtifactDefinitionInfo;
43 import org.openecomp.sdc.be.info.ArtifactTemplateInfo;
44 import org.openecomp.sdc.be.info.GroupDefinitionInfo;
45 import org.openecomp.sdc.be.model.ArtifactDefinition;
46 import org.openecomp.sdc.be.model.Component;
47 import org.openecomp.sdc.be.model.ComponentInstance;
48 import org.openecomp.sdc.be.model.ComponentParametersView;
49 import org.openecomp.sdc.be.model.DataTypeDefinition;
50 import org.openecomp.sdc.be.model.GroupDefinition;
51 import org.openecomp.sdc.be.model.GroupInstance;
52 import org.openecomp.sdc.be.model.GroupInstanceProperty;
53 import org.openecomp.sdc.be.model.GroupProperty;
54 import org.openecomp.sdc.be.model.GroupTypeDefinition;
55 import org.openecomp.sdc.be.model.PropertyDefinition;
56 import org.openecomp.sdc.be.model.PropertyDefinition.GroupInstancePropertyValueUpdateBehavior;
57 import org.openecomp.sdc.be.model.PropertyDefinition.PropertyNames;
58 import org.openecomp.sdc.be.model.Resource;
59 import org.openecomp.sdc.be.model.User;
60 import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
61 import org.openecomp.sdc.be.model.jsontitan.operations.ArtifactsOperations;
62 import org.openecomp.sdc.be.model.jsontitan.operations.GroupsOperation;
63 import org.openecomp.sdc.be.model.jsontitan.operations.TopologyTemplateOperation;
64 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
65 import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
66 import org.openecomp.sdc.be.model.operations.impl.GroupTypeOperation;
67 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
68 import org.openecomp.sdc.common.api.Constants;
69 import org.openecomp.sdc.common.util.ValidationUtils;
70 import org.openecomp.sdc.exception.ResponseFormat;
71 import org.slf4j.Logger;
72 import org.slf4j.LoggerFactory;
73 import org.springframework.beans.factory.annotation.Autowired;
74
75 import java.util.ArrayList;
76 import java.util.Arrays;
77 import java.util.Collection;
78 import java.util.Collections;
79 import java.util.EnumMap;
80 import java.util.HashMap;
81 import java.util.List;
82 import java.util.Map;
83 import java.util.Map.Entry;
84 import java.util.Optional;
85 import java.util.Set;
86 import java.util.regex.Pattern;
87 import java.util.stream.Collectors;
88
89 import static java.util.stream.Collectors.toList;
90
91 @org.springframework.stereotype.Component("groupBusinessLogic")
92 public class GroupBusinessLogic extends BaseBusinessLogic {
93     public static final String GROUP_DELIMITER_REGEX = "\\.\\.";
94     private static String ADDING_GROUP = "AddingGroup";
95
96     public static final String INITIAL_VERSION = "1";
97
98     private static final String CREATE_GROUP = "CreateGroup";
99
100     private static final String UPDATE_GROUP = "UpdateGroup";
101
102     private static final String GET_GROUP = "GetGroup";
103
104     private static final String DELETE_GROUP = "GetGroup";
105
106     private static final Logger log = LoggerFactory.getLogger(GroupBusinessLogic.class);
107
108     @javax.annotation.Resource
109     private AccessValidations accessValidations;
110
111     @javax.annotation.Resource
112     private GroupTypeOperation groupTypeOperation;
113
114     @Autowired
115     private ArtifactsOperations artifactsOperation;
116
117     @Autowired
118     private GroupsOperation groupsOperation;
119     @Autowired
120     private ApplicationDataTypeCache dataTypeCache;
121
122     private String getComponentTypeForResponse(org.openecomp.sdc.be.model.Component component) {
123         String componentTypeForResponse = "SERVICE";
124         if (component instanceof Resource) {
125             componentTypeForResponse = ((Resource) component).getResourceType().name();
126         }
127         return componentTypeForResponse;
128     }
129
130     /**
131      * Verify that the artifact members belongs to the component
132      *
133      * @param component
134      * @param artifacts
135      * @return
136      */
137     private Either<Boolean, ResponseFormat> verifyArtifactsBelongsToComponent(Component component, List<String> artifacts, String context) {
138
139         if (CollectionUtils.isEmpty(artifacts)) {
140             return Either.left(true);
141         }
142
143         Map<String, ArtifactDefinition> deploymentArtifacts = component.getDeploymentArtifacts();
144         if (MapUtils.isEmpty(deploymentArtifacts)) {
145             BeEcompErrorManager.getInstance().logInvalidInputError(context, "No deployment artifact found under component " + component.getNormalizedName(), ErrorSeverity.INFO);
146             return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
147         }
148
149         List<String> currentArtifacts = deploymentArtifacts.values().stream().map(p -> p.getUniqueId()).collect(toList());
150         log.debug("The deployment artifacts of component {} are {}", component.getNormalizedName(), deploymentArtifacts);
151         if (!currentArtifacts.containsAll(artifacts)) {
152             BeEcompErrorManager.getInstance().logInvalidInputError(context, "Not all artifacts belongs to component " + component.getNormalizedName(), ErrorSeverity.INFO);
153             return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
154         }
155
156         return Either.left(true);
157
158     }
159
160     /**
161      * verify that the members are component instances of the component
162      *
163      * @param component
164      * @param groupMembers
165      * @param memberToscaTypes
166      * @return
167      */
168     private Either<Boolean, ResponseFormat> verifyComponentInstancesAreValidMembers(Component component, String groupName, Map<String, String> groupMembers, List<String> memberToscaTypes) {
169
170         if (MapUtils.isEmpty(groupMembers)) {
171             return Either.left(true);
172         }
173
174         if (CollectionUtils.isEmpty(memberToscaTypes)) {
175             return Either.left(true);
176         }
177
178         List<ComponentInstance> componentInstances = component.getComponentInstances();
179         if (CollectionUtils.isNotEmpty(componentInstances)) {
180             Map<String, ComponentInstance> compInstUidToCompInstMap = componentInstances.stream().collect(Collectors.toMap(p -> p.getUniqueId(), p -> p));
181
182             Set<String> allCompInstances = compInstUidToCompInstMap.keySet();
183
184             for (Entry<String, String> groupMember : groupMembers.entrySet()) {
185                 String compName = groupMember.getKey();
186                 String compUid = groupMember.getValue();
187
188                 if (!allCompInstances.contains(compUid)) {
189                     /*
190                      * %1 - member name %2 - group name %3 - VF name %4 - component type [VF ]
191                      */
192                     String componentTypeForResponse = getComponentTypeForResponse(component);
193
194                     BeEcompErrorManager.getInstance().logInvalidInputError(CREATE_GROUP, "Not all group members exists under the component", ErrorSeverity.INFO);
195                     return Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_INVALID_COMPONENT_INSTANCE, compName, groupName, component.getNormalizedName(), componentTypeForResponse));
196                 }
197             }
198         }
199
200         return Either.left(true);
201     }
202
203
204
205     /**
206      * Update GroupDefinition metadata
207      *
208      * @param componentId
209      * @param user
210      * @param componentType
211      * @param updatedGroup
212      * @param inTransaction
213      * @return
214      */
215     public Either<GroupDefinition, ResponseFormat> validateAndUpdateGroupMetadata(String componentId, User user, ComponentTypeEnum componentType, GroupDefinition updatedGroup, boolean inTransaction , boolean shouldLock) {
216
217         Either<GroupDefinition, ResponseFormat> result = null;
218         try {
219             // Validate user exist
220             Either<User, ResponseFormat> validateUserExists = validateUserExists(user.getUserId(), UPDATE_GROUP, inTransaction);
221             if (validateUserExists.isRight()) {
222                 result = Either.right(validateUserExists.right().value());
223                 return result;
224             }
225             // Validate component exist
226             Either<? extends org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponent = validateComponentExists(componentId, componentType, null);
227             if (validateComponent.isRight()) {
228                 result = Either.right(validateComponent.right().value());
229                 return result;
230             }
231             org.openecomp.sdc.be.model.Component component = validateComponent.left().value();
232             // validate we can work on component
233             Either<Boolean, ResponseFormat> canWork = validateCanWorkOnComponent(component, user.getUserId());
234             if (canWork.isRight()) {
235                 result = Either.right(canWork.right().value());
236                 return result;
237             }
238             List<GroupDefinition> currentGroups = component.getGroups();
239             if (CollectionUtils.isEmpty(currentGroups)) {
240                 log.error("Failed to update the metadata of group {} on component {}. The status is {}. ", updatedGroup.getName(), component.getName(), ActionStatus.GROUP_IS_MISSING);
241                 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_IS_MISSING, updatedGroup.getName(), component.getName(), component.getComponentType().getValue()));
242                 return result;
243             }
244             // Validate groups exists in the component
245             Optional<GroupDefinition> currentGroupOpt = currentGroups.stream().filter(g -> g.getUniqueId().equals(updatedGroup.getUniqueId())).findAny();
246             if (!currentGroupOpt.isPresent()) {
247                 log.error("Failed to update the metadata of group {} on component {}. The status is {}. ", updatedGroup.getName(), component.getName(), ActionStatus.GROUP_IS_MISSING);
248                 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_IS_MISSING, updatedGroup.getName(), component.getName(), component.getComponentType().getValue()));
249                 return result;
250             }
251             GroupDefinition currentGroup = currentGroupOpt.get();
252             if ( shouldLock ){
253                 Either<Boolean, ResponseFormat> lockResult = lockComponent(componentId, component, "Update GroupDefinition Metadata");
254                 if (lockResult.isRight()) {
255                     result = Either.right(lockResult.right().value());
256                     return result;
257                 }
258             }
259             // Validate group type is vfModule
260             if (currentGroup.getType().equals(Constants.GROUP_TOSCA_HEAT)) {
261                 log.error("Failed to update the metadata of group {}. Group type is {} and cannot be updated", currentGroup.getName(), currentGroup.getType());
262                 ResponseFormat responseFormat = componentsUtils.getResponseFormat(ActionStatus.GROUP_TYPE_IS_INVALID, updatedGroup.getType());
263                 result = Either.right(responseFormat);
264                 return result;
265             }
266             result = updateGroupMetadata(component, currentGroup, updatedGroup);
267             return result;
268
269         } finally {
270             if (result.isLeft()) {
271                 titanDao.commit();
272             } else {
273                 titanDao.rollback();
274             }
275             if (shouldLock) {
276                 graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
277             }
278         }
279     }
280
281     private Either<GroupDefinition, ResponseFormat> updateGroupMetadata(Component component, GroupDefinition currentGroup, GroupDefinition updatedGroup) {
282         String currentGroupName = currentGroup.getName();
283         Either<GroupDefinition, ResponseFormat> result = validateAndUpdateGroupMetadata(currentGroup, updatedGroup);
284
285         if (result.isRight()) {
286             log.debug("Failed to validate a metadata of the group {} on component {}. ", updatedGroup.getName(), component.getName());
287         }
288         if (result.isLeft()) {
289             result = updateGroup(component, currentGroup, currentGroupName);
290         }
291         return result;
292     }
293
294     private Either<GroupDefinition, ResponseFormat> updateGroup(Component component, GroupDefinition updatedGroup, String currentGroupName) {
295         Either<GroupDefinition, StorageOperationStatus> handleGroupRes;
296         Either<GroupDefinition, ResponseFormat> result = null;
297         if (updatedGroup.getName().equals(currentGroupName)) {
298             handleGroupRes = groupsOperation.updateGroup(component, updatedGroup);
299             if (handleGroupRes.isRight()) {
300                 log.debug("Failed to update a metadata of the group {} on component {}. ", updatedGroup.getName(), component.getName());
301                 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(handleGroupRes.right().value())));
302             }
303         } else {
304             StorageOperationStatus deleteStatus = groupsOperation.deleteGroup(component, currentGroupName);
305             if (deleteStatus != StorageOperationStatus.OK) {
306                 log.debug("Failed to delete the group {} from component {}. ", updatedGroup.getName(), component.getName());
307                 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(deleteStatus)));
308             }
309             handleGroupRes = groupsOperation.addGroup(component, updatedGroup);
310             if (handleGroupRes.isRight()) {
311                 log.debug("Failed to add the group {} to component {}. ", updatedGroup.getName(), component.getName());
312                 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(handleGroupRes.right().value())));
313             }
314         }
315         if (result == null) {
316             result = Either.left(updatedGroup);
317         }
318         return result;
319     }
320
321     /**
322      * Validate and Update Group Property
323      *
324      * @param componentId
325      * @param groupUniqueId
326      * @param user
327      * @param componentType
328      * @param groupPropertiesToUpdate
329      * @param inTransaction
330      * @return
331      */
332     public Either<List<GroupProperty>, ResponseFormat> validateAndUpdateGroupProperties(String componentId, String groupUniqueId, User user, ComponentTypeEnum componentType, List<GroupProperty> groupPropertiesToUpdate, boolean inTransaction) {
333
334         Either<List<GroupProperty>, ResponseFormat> result = Either.left(groupPropertiesToUpdate);
335         try {
336             Optional<GroupDefinition> optionalGroupConnectedToVf = null;
337             GroupDefinition currentGroup = null;
338             StorageOperationStatus lockResult = graphLockOperation.lockComponent(componentId, componentType.getNodeType());
339             if (lockResult != StorageOperationStatus.OK) {
340                 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(lockResult, componentType), componentId));
341             }
342             if (result.isLeft()) {
343                 // VF exist because lock succedded
344                 Resource vf = (Resource) toscaOperationFacade.getToscaElement(componentId).left().value();
345                 optionalGroupConnectedToVf =
346                         // All groups on resource
347                         vf.getGroups().stream().
348                         // Filter in group sent is part of VF groups
349                                 filter(e -> e.getUniqueId().equals(groupUniqueId)).
350                                 // Collect
351                                 findAny();
352                 if (!optionalGroupConnectedToVf.isPresent()) {
353                     result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_IS_MISSING, groupUniqueId, vf.getName(), ComponentTypeEnum.RESOURCE.getValue()));
354                 }
355             }
356
357             if (result.isLeft()) {
358                 currentGroup = optionalGroupConnectedToVf.get();
359                 result = validateGroupPropertyAndResetEmptyValue(currentGroup, groupPropertiesToUpdate);
360             }
361             if (result.isLeft()) {
362                 result = updateGroupPropertiesValue(componentId, currentGroup, groupPropertiesToUpdate, inTransaction);
363                 if (result.isRight()) {
364                     BeEcompErrorManager.getInstance().logBeSystemError("Update GroupProperties");
365                     log.debug("failed to update Vf {}", componentId);
366                 }
367             }
368
369         } catch (Exception e) {
370             log.debug("Error in validateAndUpdateGroupProperty {}", e);
371             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
372         } finally {
373             graphLockOperation.unlockComponent(componentId, componentType.getNodeType());
374         }
375         return result;
376     }
377
378     private void resetEmptyValueWithDefaults(List<GroupProperty> groupPropertiesToUpdate, GroupDefinition originalGroup) {
379         Map<String, GroupProperty> originalProperties =
380                 // Stream of original properties from group
381                 originalGroup.convertToGroupProperties().stream().
382                 // Collecting to map with name as key
383                         collect(Collectors.toMap(e -> e.getName(), e -> e));
384         for (GroupProperty gp : groupPropertiesToUpdate) {
385             if (StringUtils.isEmpty(gp.getValue())) {
386                 gp.setValue(originalProperties.get(gp.getName()).getDefaultValue());
387             }
388         }
389
390     }
391
392     private Either<List<GroupProperty>, ResponseFormat> validateGroupPropertyAndResetEmptyValue(GroupDefinition originalGroup, List<GroupProperty> groupPropertiesToUpdate) {
393
394         Either<List<GroupProperty>, ResponseFormat> ret = validateOnlyValueChanged(groupPropertiesToUpdate, originalGroup);
395         if (ret.isLeft()) {
396             resetEmptyValueWithDefaults(groupPropertiesToUpdate, originalGroup);
397         }
398         if (ret.isLeft()) {
399             // Validate Type Match Value
400             Optional<StorageOperationStatus> optionalError =
401                     // Stream of group properties
402                     groupPropertiesToUpdate.stream().
403                     // Validate each and map to returned Strorage status value
404                             map(e -> groupOperation.validateAndUpdatePropertyValue(e)).
405                             // Keep only failed result if there is such
406                             filter(e -> e != StorageOperationStatus.OK).
407                             // collect
408                             findFirst();
409             if (optionalError.isPresent()) {
410                 ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(optionalError.get());
411                 ret = Either.right(componentsUtils.getResponseFormat(actionStatus));
412             }
413
414         }
415         if (ret.isLeft()) {
416             // Validate min max ect...
417             ret = validatePropertyBusinessLogic(groupPropertiesToUpdate, originalGroup);
418         }
419
420         return ret;
421     }
422
423     private Either<List<GroupProperty>, ResponseFormat> validatePropertyBusinessLogic(List<GroupProperty> groupPropertiesToUpdate, GroupDefinition originalGroup) {
424
425         Either<List<GroupProperty>, ResponseFormat> ret = Either.left(groupPropertiesToUpdate);
426
427         Map<PropertyNames, String> nameValueMap = new HashMap<>();
428         for (GroupProperty gp : groupPropertiesToUpdate) {
429             // Filter out non special properties which does not have Enum
430             final PropertyNames gpEnum = PropertyNames.findName(gp.getName());
431             if (gpEnum != null) {
432                 nameValueMap.put(gpEnum, gp.getValue());
433             }
434         }
435
436         if (!MapUtils.isEmpty(nameValueMap)) {
437
438             if (nameValueMap.containsKey(PropertyNames.INITIAL_COUNT) || nameValueMap.containsKey(PropertyNames.MAX_INSTANCES) || nameValueMap.containsKey(PropertyNames.MIN_INSTANCES)) {
439
440                 Map<PropertyNames, String> oldValueMap = prepareMapWithOriginalProperties(originalGroup);
441
442                 Either<Boolean, ResponseFormat> eitherValid = validateMinMaxAndInitialCountPropertyLogicVF(nameValueMap, oldValueMap);
443                 if (eitherValid.isRight()) {
444                     ret = Either.right(eitherValid.right().value());
445                 }
446             }
447             if (ret.isLeft() && (nameValueMap.containsKey(PropertyNames.VF_MODULE_DESCRIPTION) || nameValueMap.containsKey(PropertyNames.VF_MODULE_LABEL))) {
448
449                 Optional<ResponseFormat> optionalError =
450                         // Stream of group Properties
451                         groupPropertiesToUpdate.stream().
452                         // Filter in only properties that needs text validation
453                                 filter(e -> enumHasValueFilter(e.getName(), enumName -> PropertyNames.findName(enumName), PropertyNames.VF_MODULE_DESCRIPTION, PropertyNames.VF_MODULE_LABEL)).
454                                 // validate text properties
455                                 map(e -> validateFreeText(e)).
456                                 // filter in only errors if exist
457                                 filter(e -> e.isRight()).
458                                 // map the Either value to the Error
459                                 map(e -> e.right().value())
460                                 // collect
461                                 .findFirst();
462                 if (optionalError.isPresent()) {
463                     ret = Either.right(optionalError.get());
464                 }
465
466             }
467         }
468
469         return ret;
470     }
471
472     private Map<PropertyNames, String> prepareMapWithOriginalProperties(GroupDefinition originalGroup) {
473         Map<PropertyNames, String> oldValueMap = new HashMap<>();
474         PropertyNames[] propertiesToCheck = new PropertyNames[] { PropertyNames.INITIAL_COUNT, PropertyNames.MAX_INSTANCES, PropertyNames.MIN_INSTANCES };
475
476         for (GroupProperty gp : originalGroup.convertToGroupProperties()) {
477             if (enumHasValueFilter(gp.getName(), PropertyNames::findName, propertiesToCheck)) {
478                 oldValueMap.put(PropertyNames.findName(gp.getName()), gp.getValue());
479             }
480         }
481         if (StringUtils.isEmpty(oldValueMap.get(PropertyNames.MAX_INSTANCES))) {
482             oldValueMap.put(PropertyNames.MAX_INSTANCES, String.valueOf(Integer.MAX_VALUE));
483         }
484         return oldValueMap;
485     }
486
487     private Either<List<GroupProperty>, ResponseFormat> validateOnlyValueChanged(List<GroupProperty> groupPropertiesToUpdate, GroupDefinition originalGroup) {
488
489         Either<List<GroupProperty>, ResponseFormat> ret = Either.left(groupPropertiesToUpdate);
490         if (CollectionUtils.isEmpty(groupPropertiesToUpdate)) {
491             ret = Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, StringUtils.EMPTY));
492         } else if (CollectionUtils.isEmpty(originalGroup.getProperties())) {
493             ret = Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, groupPropertiesToUpdate.get(NumberUtils.INTEGER_ZERO).getName()));
494         } else {
495             Map<String, GroupProperty> namePropertyMap =
496                     // Original Group Properties Stream
497                     originalGroup.convertToGroupProperties().stream().
498                     // Collect to map with name as key
499                             collect(Collectors.toMap(e -> e.getName(), e -> e));
500
501             Optional<GroupProperty> optionalMissingProperty =
502                     // Group Properties to be updated Stream
503                     groupPropertiesToUpdate.stream().
504                     // Filter in property that is not contained in original if there is such
505                             filter(e -> !namePropertyMap.containsKey(e.getName())).
506                             // collect
507                             findFirst();
508
509             if (optionalMissingProperty.isPresent()) {
510                 ret = Either.right(componentsUtils.getResponseFormat(ActionStatus.PROPERTY_NOT_FOUND, optionalMissingProperty.get().getName()));
511             } else {
512                 Optional<GroupProperty> optionalNonValueChange =
513                         // groups to be updated stream
514                         groupPropertiesToUpdate.stream().
515                         // filter in only properties with non-value (illegal) change
516                                 filter(e -> !isOnlyGroupPropertyValueChanged(e, namePropertyMap.get(e.getName()))).
517                                 // Collect
518                                 findFirst();
519                 if (optionalNonValueChange.isPresent()) {
520                     ret = Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_PROPERTY, optionalNonValueChange.get().getName()));
521
522                 }
523             }
524
525         }
526         return ret;
527     }
528
529     /**
530      * if groupProperty are the same or if only value is different returns true, otherwise returns false.
531      *
532      * @param groupProperty
533      * @param groupProperty2
534      * @return
535      */
536     private boolean isOnlyGroupPropertyValueChanged(GroupProperty groupProperty, GroupProperty groupProperty2) {
537         // Create 2 duplicates for groupPropery and reset their values
538         try {
539             GroupProperty groupPropertyDuplicate = new GroupProperty(groupProperty);
540             groupPropertyDuplicate.setValue(null);
541             groupPropertyDuplicate.setSchema(null);
542             groupPropertyDuplicate.setParentUniqueId(null);
543             GroupProperty groupProperty2Duplicate = new GroupProperty(groupProperty2);
544             groupProperty2Duplicate.setValue(null);
545             groupProperty2Duplicate.setSchema(null);
546             groupProperty2Duplicate.setParentUniqueId(null);
547             return groupPropertyDuplicate.equals(groupProperty2Duplicate) && StringUtils.equals(groupPropertyDuplicate.getValueUniqueUid(), groupProperty2Duplicate.getValueUniqueUid());
548         } catch (Exception e) {
549             log.debug("Failed validate group properties. ", e);
550             return false;
551         }
552     }
553
554     /**
555      * Validate and update GroupDefinition metadata
556      *
557      * @param currentGroup
558      * @param groupUpdate
559      * @return
560      **/
561     private Either<GroupDefinition, ResponseFormat> validateAndUpdateGroupMetadata(GroupDefinition currentGroup, GroupDefinition groupUpdate) {
562         // Check if to update, and update GroupDefinition name.
563         Either<Boolean, ResponseFormat> response = validateAndUpdateGroupName(currentGroup, groupUpdate);
564         if (response.isRight()) {
565             ResponseFormat errorResponse = response.right().value();
566             return Either.right(errorResponse);
567         }
568
569         // Do not allow to update GroupDefinition version directly.
570         String versionUpdated = groupUpdate.getVersion();
571         String versionCurrent = currentGroup.getVersion();
572         if (versionUpdated != null && !versionCurrent.equals(versionUpdated)) {
573             log.info("update Group: recived request to update version to {} the field is not updatable ignoring.", versionUpdated);
574         }
575
576         return Either.left(currentGroup);
577     }
578
579     /**
580      * Validate and update GroupDefinition name
581      *
582      * @param currentGroup
583      * @param groupUpdate
584      * @return
585      */
586     private Either<Boolean, ResponseFormat> validateAndUpdateGroupName(GroupDefinition currentGroup, GroupDefinition groupUpdate) {
587         String nameUpdated = groupUpdate.getName();
588         String nameCurrent = currentGroup.getName();
589         if (!nameCurrent.equals(nameUpdated)) {
590             Either<Boolean, ResponseFormat> validatNameResponse = validateGroupName(currentGroup.getName(), groupUpdate.getName() ,true);
591             if (validatNameResponse.isRight()) {
592                 ResponseFormat errorRespons = validatNameResponse.right().value();
593                 return Either.right(errorRespons);
594             }
595             currentGroup.setName(groupUpdate.getName());
596         }
597         return Either.left(true);
598     }
599
600     /**
601      * Validate that group name to update is valid (same as current group name except for middle part). For example: Current group name: MyResource..MyDesc..Module-1 Group to update: MyResource..MyDesc2..Module-1 Verify that only the second part
602      * MyDesc was changed.
603      *
604      * @param currentGroupName
605      * @param groupUpdateName
606      * @return
607      */
608     private Either<Boolean, ResponseFormat> validateGroupName(String currentGroupName, String groupUpdateName , boolean isforceNameModification) {
609         try {
610             // Check if the group name is in old format.
611             if (Pattern.compile(Constants.MODULE_OLD_NAME_PATTERN).matcher(groupUpdateName).matches()) {
612                 log.error("Group name {} is in old format", groupUpdateName);
613                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_VF_MODULE_NAME, groupUpdateName));
614             }
615
616             // Check that name pats 1 and 3 did not changed (only the second
617             // part can be changed)
618             // But verify before that the current group format is the new one
619             if (!Pattern.compile(Constants.MODULE_OLD_NAME_PATTERN).matcher(currentGroupName).matches()) {
620                 String[] split1 = currentGroupName.split(GROUP_DELIMITER_REGEX);
621                 String currentResourceName = split1[0];
622                 String currentCounter = split1[2];
623
624                 String[] split2 = groupUpdateName.split(GROUP_DELIMITER_REGEX);
625                 String groupUpdateResourceName = split2[0];
626                 String groupUpdateCounter = split2[2];
627                 if (!isforceNameModification){            //if not forced ,allow name prefix&suffix validation [no changes]
628                     if (!currentResourceName.equals(groupUpdateResourceName)) {
629                         return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_VF_MODULE_NAME_MODIFICATION, currentResourceName));
630                     }
631
632                     if (!currentCounter.equals(groupUpdateCounter)) {
633                         return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_VF_MODULE_NAME_MODIFICATION, currentCounter));
634                     }
635                 }
636
637             }
638
639             return Either.left(true);
640         } catch (Exception e) {
641             log.error("Error valiadting group name", e);
642             return Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_CONTENT));
643         }
644     }
645
646
647     /**
648      * associate artifacts to a given group
649      *
650      * @param componentId
651      * @param userId
652      * @param componentType
653      * @param inTransaction
654      * @return
655      */
656     public Either<GroupDefinitionInfo, ResponseFormat> getGroupWithArtifactsById(ComponentTypeEnum componentType, String componentId, String groupId, String userId, boolean inTransaction) {
657
658         Either<GroupDefinitionInfo, ResponseFormat> result = null;
659
660         // Validate user exist
661         Either<User, ResponseFormat> validateUserExists = validateUserExists(userId, GET_GROUP, true);
662
663         if (validateUserExists.isRight()) {
664             result = Either.right(validateUserExists.right().value());
665             return result;
666         }
667
668         // Validate component exist
669         org.openecomp.sdc.be.model.Component component = null;
670         String realComponentId = componentId;
671
672         try {
673             ComponentParametersView componentParametersView = new ComponentParametersView();
674             componentParametersView.disableAll();
675             componentParametersView.setIgnoreGroups(false);
676             componentParametersView.setIgnoreArtifacts(false);
677             componentParametersView.setIgnoreUsers(false);
678
679             Either<? extends org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponent = validateComponentExists(realComponentId, componentType, componentParametersView);
680             if (validateComponent.isRight()) {
681                 result = Either.right(validateComponent.right().value());
682                 return result;
683             }
684             component = validateComponent.left().value();
685
686             Either<GroupDefinition, StorageOperationStatus> groupEither = findGroupOnComponent(component, groupId);
687
688             if (groupEither.isRight()) {
689                 log.debug("Faild to find group {} under component {}", groupId, component.getUniqueId());
690                 BeEcompErrorManager.getInstance().logInvalidInputError(GET_GROUP, "group  " + groupId + " not found under component " + component.getUniqueId(), ErrorSeverity.INFO);
691                 String componentTypeForResponse = getComponentTypeForResponse(component);
692                 result = Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_IS_MISSING, groupId, component.getSystemName(), componentTypeForResponse));
693                 return result;
694             }
695             GroupDefinition group = groupEither.left().value();
696
697             Boolean isBase = null;
698             List<GroupProperty> props = group.convertToGroupProperties();
699             if (props != null && !props.isEmpty()) {
700                 Optional<GroupProperty> isBasePropOp = props.stream().filter(p -> p.getName().equals(Constants.IS_BASE)).findAny();
701                 if (isBasePropOp.isPresent()) {
702                     GroupProperty propIsBase = isBasePropOp.get();
703                     isBase = Boolean.parseBoolean(propIsBase.getValue());
704
705                 } else {
706                     BeEcompErrorManager.getInstance().logInvalidInputError(GET_GROUP, "failed to find prop isBase " + component.getNormalizedName(), ErrorSeverity.INFO);
707                 }
708             }
709
710             List<ArtifactDefinitionInfo> artifacts = new ArrayList<>();
711             List<ArtifactDefinition> artifactsFromComponent = new ArrayList<>();
712             List<String> artifactsIds = group.getArtifacts();
713
714             Map<String, ArtifactDefinition> deploymentArtifacts = null;
715             if (MapUtils.isNotEmpty(component.getDeploymentArtifacts())) {
716                 deploymentArtifacts = component.getDeploymentArtifacts().values().stream().collect(Collectors.toMap(a -> a.getUniqueId(), a -> a));
717             }
718
719             if (artifactsIds != null && !artifactsIds.isEmpty()) {
720                 for (String id : artifactsIds) {
721                     if (MapUtils.isEmpty(deploymentArtifacts) || !deploymentArtifacts.containsKey(id)) {
722                         log.debug("Failed to get artifact {} . Status is {} ", id, StorageOperationStatus.NOT_FOUND);
723                         ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(StorageOperationStatus.NOT_FOUND));
724                         result = Either.right(responseFormat);
725                         return result;
726                     }
727                     artifactsFromComponent.add(deploymentArtifacts.get(id));
728                 }
729                 if (!artifactsFromComponent.isEmpty()) {
730                     for (ArtifactDefinition artifactDefinition : artifactsFromComponent) {
731                         ArtifactDefinitionInfo artifactDefinitionInfo = new ArtifactDefinitionInfo(artifactDefinition);
732                         artifacts.add(artifactDefinitionInfo);
733                     }
734                 }
735
736             }
737             GroupDefinitionInfo resultInfo = new GroupDefinitionInfo(group);
738             resultInfo.setIsBase(isBase);
739             if (!artifacts.isEmpty()) {
740                 resultInfo.setArtifacts(artifacts);
741             }
742             result = Either.left(resultInfo);
743
744             return result;
745
746         } finally {
747
748             if (!inTransaction) {
749
750                 if (result == null || result.isRight()) {
751                     log.debug("Going to execute rollback on create group.");
752                     titanDao.rollback();
753                 } else {
754                     log.debug("Going to execute commit on create group.");
755                     titanDao.commit();
756                 }
757
758             }
759
760         }
761
762     }
763
764     private Either<GroupDefinition, StorageOperationStatus> findGroupOnComponent(Component component, String groupId) {
765
766         Either<GroupDefinition, StorageOperationStatus> result = null;
767         if (CollectionUtils.isNotEmpty(component.getGroups())) {
768             Optional<GroupDefinition> foundGroup = component.getGroups().stream().filter(g -> g.getUniqueId().equals(groupId)).findFirst();
769             if (foundGroup.isPresent()) {
770                 result = Either.left(foundGroup.get());
771             }
772         }
773         if (result == null) {
774             result = Either.right(StorageOperationStatus.NOT_FOUND);
775         }
776         return result;
777     }
778
779     public String getAsString(List<String> list) {
780
781         if (list == null || list.isEmpty()) {
782             return "";
783         }
784         StringBuilder builder = new StringBuilder();
785         list.forEach(p -> builder.append(p + ","));
786
787         String result = builder.toString();
788         return result.substring(0, result.length());
789
790     }
791
792
793     private Either<List<GroupProperty>, ResponseFormat> updateGroupPropertiesValue(String componentId, GroupDefinition currentGroup, List<GroupProperty> groupPropertyToUpdate, boolean inTransaction) {
794         Either<List<GroupProperty>, ResponseFormat> result;
795
796         Either<List<GroupProperty>, StorageOperationStatus> eitherUpdate = groupsOperation.updateGroupPropertiesOnComponent(componentId, currentGroup, groupPropertyToUpdate);
797         if (eitherUpdate.isRight()) {
798             ActionStatus actionStatus = componentsUtils.convertFromStorageResponse(eitherUpdate.right().value());
799             result = Either.right(componentsUtils.getResponseFormat(actionStatus));
800         } else {
801             result = Either.left(eitherUpdate.left().value());
802         }
803         return result;
804     }
805
806     public Either<Boolean, ResponseFormat> validateGenerateVfModuleGroupNames(List<ArtifactTemplateInfo> allGroups, String resourceSystemName, int startGroupCounter) {
807         Either<Boolean, ResponseFormat> validateGenerateGroupNamesRes = Either.left(true);
808         Collections.sort(allGroups, (art1, art2) -> ArtifactTemplateInfo.compareByGroupName(art1, art2));
809         for (ArtifactTemplateInfo group : allGroups) {
810             Either<String, ResponseFormat> validateGenerateGroupNameRes = validateGenerateVfModuleGroupName(resourceSystemName, group.getDescription(), startGroupCounter++);
811             if (validateGenerateGroupNameRes.isRight()) {
812                 validateGenerateGroupNamesRes = Either.right(validateGenerateGroupNameRes.right().value());
813                 break;
814             }
815             group.setGroupName(validateGenerateGroupNameRes.left().value());
816         }
817         return validateGenerateGroupNamesRes;
818     }
819
820     /**
821      * Generate module name from resourceName, description and counter
822      *
823      * @param resourceSystemName
824      * @param description
825      * @param groupCounter
826      * @return
827      */
828     private Either<String, ResponseFormat> validateGenerateVfModuleGroupName(String resourceSystemName, String description, int groupCounter) {
829         Either<String, ResponseFormat> validateGenerateGroupNameRes;
830         if (resourceSystemName != null && description != null && Pattern.compile(Constants.MODULE_DESC_PATTERN).matcher(description).matches()) {
831             final String fileName = description.replaceAll(GROUP_DELIMITER_REGEX, "\\.");
832             validateGenerateGroupNameRes = Either.left(String.format(Constants.MODULE_NAME_FORMAT, resourceSystemName, FilenameUtils.removeExtension(fileName), groupCounter));
833         } else {
834             validateGenerateGroupNameRes = Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_VF_MODULE_NAME));
835         }
836         return validateGenerateGroupNameRes;
837     }
838
839     public Either<Map<String, GroupDefinition>, ResponseFormat> validateUpdateVfGroupNames(Map<String, GroupDefinition> groups, String resourceSystemName) {
840
841         Map<String, GroupDefinition> updatedNamesGroups = new HashMap<>();
842         Either<Map<String, GroupDefinition>, ResponseFormat> result = Either.left(updatedNamesGroups);
843         for (Entry<String, GroupDefinition> groupEntry : groups.entrySet()) {
844             GroupDefinition curGroup = groupEntry.getValue();
845             String groupType = curGroup.getType();
846             String groupName = groupEntry.getKey();
847             int counter;
848             String description;
849             Either<String, ResponseFormat> newGroupNameRes;
850             if (groupType.equals(Constants.DEFAULT_GROUP_VF_MODULE) && !Pattern.compile(Constants.MODULE_NEW_NAME_PATTERN).matcher(groupName).matches()) {
851
852                 if (Pattern.compile(Constants.MODULE_OLD_NAME_PATTERN).matcher(groupEntry.getKey()).matches()) {
853                     counter = Integer.parseInt(groupEntry.getKey().split(Constants.MODULE_NAME_DELIMITER)[1]);
854                     description = curGroup.getDescription();
855                 } else {
856                     counter = getNextVfModuleNameCounter(updatedNamesGroups);
857                     description = groupName;
858                 }
859                 newGroupNameRes = validateGenerateVfModuleGroupName(resourceSystemName, description, counter);
860                 if (newGroupNameRes.isRight()) {
861                     log.debug("Failed to generate new vf module group name. Status is {} ", newGroupNameRes.right().value());
862                     result = Either.right(newGroupNameRes.right().value());
863                     break;
864                 }
865                 groupName = newGroupNameRes.left().value();
866                 curGroup.setName(groupName);
867             }
868             updatedNamesGroups.put(groupName, curGroup);
869         }
870         return result;
871     }
872
873     public int getNextVfModuleNameCounter(Map<String, GroupDefinition> groups) {
874         int counter = 0;
875         if (groups != null && !groups.isEmpty()) {
876             counter = getNextVfModuleNameCounter(groups.values());
877         }
878         return counter;
879     }
880
881     public int getNextVfModuleNameCounter(Collection<GroupDefinition> groups) {
882         int counter = 0;
883         if (groups != null && !groups.isEmpty()) {
884             List<Integer> counters = groups.stream().filter(group -> Pattern.compile(Constants.MODULE_NEW_NAME_PATTERN).matcher(group.getName()).matches() || Pattern.compile(Constants.MODULE_OLD_NAME_PATTERN).matcher(group.getName()).matches())
885                     .map(group -> Integer.parseInt(group.getName().split(Constants.MODULE_NAME_DELIMITER)[1])).collect(toList());
886             counter = (counters == null || counters.isEmpty()) ? 0 : counters.stream().max((a, b) -> Integer.compare(a, b)).get() + 1;
887         }
888         return counter;
889     }
890
891     public Either<List<GroupDefinition>, ResponseFormat> validateUpdateVfGroupNamesOnGraph(List<GroupDefinition> groups, Component component, boolean inTransaction) {
892         List<GroupDefinition> updatedGroups = new ArrayList<>();
893         Either<List<GroupDefinition>, ResponseFormat> result = Either.left(updatedGroups);
894
895         for (GroupDefinition group : groups) {
896             String groupType = group.getType();
897             String oldGroupName = group.getName();
898             String newGroupName;
899             Either<String, ResponseFormat> newGroupNameRes;
900             int counter;
901             if (groupType.equals(Constants.DEFAULT_GROUP_VF_MODULE) && Pattern.compile(Constants.MODULE_OLD_NAME_PATTERN).matcher(oldGroupName).matches()) {
902                 counter = Integer.parseInt(group.getName().split(Constants.MODULE_NAME_DELIMITER)[1]);
903                 newGroupNameRes = validateGenerateVfModuleGroupName(component.getSystemName(), group.getDescription(), counter);
904                 if (newGroupNameRes.isRight()) {
905                     log.debug("Failed to generate new vf module group name. Status is {} ", newGroupNameRes.right().value());
906                     result = Either.right(newGroupNameRes.right().value());
907                     break;
908                 }
909                 newGroupName = newGroupNameRes.left().value();
910                 group.setName(newGroupName);
911
912             }
913             updatedGroups.add(group);
914
915         }
916
917         result = Either.left(updatedGroups);
918         return result;
919     }
920
921
922     public Either<GroupDefinitionInfo, ResponseFormat> getGroupInstWithArtifactsById(ComponentTypeEnum componentType, String componentId, String componentInstanceId, String groupInstId, String userId, boolean inTransaction) {
923         Either<GroupDefinitionInfo, ResponseFormat> result = null;
924
925         // Validate user exist
926         Either<User, ResponseFormat> validateUserExists = validateUserExists(userId, UPDATE_GROUP, true);
927
928         if (validateUserExists.isRight()) {
929             result = Either.right(validateUserExists.right().value());
930             return result;
931         }
932
933         // Validate component exist
934         org.openecomp.sdc.be.model.Component component = null;
935         String realComponentId = componentId;
936
937         try {
938             ComponentParametersView componentParametersView = new ComponentParametersView();
939             componentParametersView.disableAll();
940             componentParametersView.setIgnoreUsers(false);
941             componentParametersView.setIgnoreComponentInstances(false);
942             componentParametersView.setIgnoreArtifacts(false);
943
944             Either<? extends org.openecomp.sdc.be.model.Component, ResponseFormat> validateComponent = validateComponentExists(realComponentId, componentType, componentParametersView);
945             if (validateComponent.isRight()) {
946                 result = Either.right(validateComponent.right().value());
947                 return result;
948             }
949             component = validateComponent.left().value();
950             Either<ImmutablePair<ComponentInstance, GroupInstance>, StorageOperationStatus> findComponentInstanceAndGroupInstanceRes = findComponentInstanceAndGroupInstanceOnComponent(component, componentInstanceId, groupInstId);
951
952             if (findComponentInstanceAndGroupInstanceRes.isRight()) {
953                 log.debug("Failed to get group {} . Status is {} ", groupInstId, findComponentInstanceAndGroupInstanceRes.right().value());
954                 ResponseFormat responseFormat = componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(findComponentInstanceAndGroupInstanceRes.right().value()));
955                 result = Either.right(responseFormat);
956                 return result;
957             }
958
959             GroupInstance group = findComponentInstanceAndGroupInstanceRes.left().value().getRight();
960
961             Boolean isBase = null;
962             List<? extends GroupProperty> props = group.convertToGroupInstancesProperties();
963             if (props != null && !props.isEmpty()) {
964                 Optional<? extends GroupProperty> isBasePropOp = props.stream().filter(p -> p.getName().equals(Constants.IS_BASE)).findAny();
965                 if (isBasePropOp.isPresent()) {
966                     GroupProperty propIsBase = isBasePropOp.get();
967                     isBase = Boolean.parseBoolean(propIsBase.getValue());
968
969                 } else {
970                     BeEcompErrorManager.getInstance().logInvalidInputError(GET_GROUP, "failed to find prop isBase " + component.getNormalizedName(), ErrorSeverity.INFO);
971                 }
972             }
973
974             List<ArtifactDefinitionInfo> artifacts = new ArrayList<>();
975             List<String> artifactsIds = group.getArtifacts();
976             if (artifactsIds != null && !artifactsIds.isEmpty()) {
977
978                 List<ComponentInstance> instances = component.getComponentInstances();
979                 if (instances != null) {
980                     Optional<ComponentInstance> findFirst = instances.stream().filter(i -> i.getUniqueId().equals(componentInstanceId)).findFirst();
981                     if (findFirst.isPresent()) {
982                         ComponentInstance ci = findFirst.get();
983                         Map<String, ArtifactDefinition> deploymentArtifacts = ci.getDeploymentArtifacts();
984                         for (String id : artifactsIds) {
985                             Optional<ArtifactDefinition> artOp = deploymentArtifacts.values().stream().filter(a -> a.getUniqueId().equals(id)).findFirst();
986                             if (artOp.isPresent()) {
987                                 artifacts.add(new ArtifactDefinitionInfo(artOp.get()));
988                             }
989                         }
990                         List<String> instArtifactsIds = group.getGroupInstanceArtifacts();
991                         for (String id : instArtifactsIds) {
992                             Optional<ArtifactDefinition> artOp = deploymentArtifacts.values().stream().filter(a -> a.getUniqueId().equals(id)).findFirst();
993                             if (artOp.isPresent()) {
994                                 artifacts.add(new ArtifactDefinitionInfo(artOp.get()));
995                             }
996                         }
997                     }
998
999                 }
1000             }
1001             GroupDefinitionInfo resultInfo = new GroupDefinitionInfo(group);
1002             resultInfo.setIsBase(isBase);
1003             if (!artifacts.isEmpty()) {
1004                 resultInfo.setArtifacts(artifacts);
1005             }
1006             result = Either.left(resultInfo);
1007
1008             return result;
1009
1010         } finally {
1011
1012             if (!inTransaction) {
1013
1014                 if (result == null || result.isRight()) {
1015                     log.debug("Going to execute rollback on create group.");
1016                     titanDao.rollback();
1017                 } else {
1018                     log.debug("Going to execute commit on create group.");
1019                     titanDao.commit();
1020                 }
1021
1022             }
1023
1024         }
1025     }
1026
1027     private Either<ImmutablePair<ComponentInstance, GroupInstance>, StorageOperationStatus> findComponentInstanceAndGroupInstanceOnComponent(Component component, String componentInstanceId, String groupInstId) {
1028
1029         Either<ImmutablePair<ComponentInstance, GroupInstance>, StorageOperationStatus> result = null;
1030         if (CollectionUtils.isNotEmpty(component.getComponentInstances())) {
1031             Optional<GroupInstance> foundGroup;
1032             Optional<ComponentInstance> foundComponent = component.getComponentInstances().stream().filter(ci -> ci.getUniqueId().equals(componentInstanceId)).findFirst();
1033             if (foundComponent.isPresent() && CollectionUtils.isNotEmpty(foundComponent.get().getGroupInstances())) {
1034                 foundGroup = foundComponent.get().getGroupInstances().stream().filter(gi -> gi.getUniqueId().equals(groupInstId)).findFirst();
1035                 if (foundGroup.isPresent()) {
1036                     result = Either.left(new ImmutablePair<>(foundComponent.get(), foundGroup.get()));
1037                 }
1038             }
1039         }
1040         if (result == null) {
1041             result = Either.right(StorageOperationStatus.NOT_FOUND);
1042         }
1043         return result;
1044     }
1045
1046     private int getLatestIntProperty(Map<PropertyNames, String> newValues, Map<PropertyNames, String> parentValues, PropertyNames propertyKey) {
1047         String value;
1048         if (newValues.containsKey(propertyKey)) {
1049             value = newValues.get(propertyKey);
1050         } else {
1051             value = parentValues.get(propertyKey);
1052         }
1053         return Integer.valueOf(value);
1054     }
1055
1056     private boolean isPropertyChanged(Map<PropertyNames, String> newValues, Map<PropertyNames, String> parentValues, final PropertyNames minInstances) {
1057         return newValues.containsKey(minInstances) && !newValues.get(minInstances).equals(parentValues.get(minInstances));
1058     }
1059
1060     private Either<Boolean, ResponseFormat> validateMinMaxAndInitialCountPropertyLogicVF(Map<PropertyNames, String> newValues, Map<PropertyNames, String> parentValues) {
1061
1062         int latestMaxInstances = getLatestIntProperty(newValues, parentValues, PropertyNames.MAX_INSTANCES);
1063         int latestInitialCount = getLatestIntProperty(newValues, parentValues, PropertyNames.INITIAL_COUNT);
1064         int latestMinInstances = getLatestIntProperty(newValues, parentValues, PropertyNames.MIN_INSTANCES);
1065         Either<Boolean, ResponseFormat> result = Either.left(true);
1066
1067         if (isPropertyChanged(newValues, parentValues, PropertyNames.INITIAL_COUNT) && result.isLeft()
1068                 && (latestInitialCount > latestMaxInstances || latestInitialCount < latestMinInstances)) {
1069             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_GROUP_INITIAL_COUNT_PROPERTY_VALUE, PropertyNames.INITIAL_COUNT.getPropertyName(), String.valueOf(latestMinInstances), String.valueOf(latestMaxInstances)));
1070         }
1071         if (isPropertyChanged(newValues, parentValues, PropertyNames.MAX_INSTANCES) && result.isLeft() &&
1072                 latestMaxInstances < latestInitialCount) {
1073             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_GROUP_PROPERTY_VALUE_LOWER_HIGHER, PropertyNames.MAX_INSTANCES.getPropertyName(), "higher", String.valueOf(latestInitialCount)));
1074         }
1075         if (isPropertyChanged(newValues, parentValues, PropertyNames.MIN_INSTANCES) &&
1076                 result.isLeft() && latestMinInstances > latestInitialCount) {
1077             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_GROUP_PROPERTY_VALUE_LOWER_HIGHER, PropertyNames.MIN_INSTANCES.getPropertyName(), "lower", String.valueOf(latestInitialCount)));
1078         }
1079         return result;
1080     }
1081
1082     private Either<Boolean, ResponseFormat> validateMinMaxAndInitialCountPropertyLogic(Map<PropertyNames, String> newValues, Map<PropertyNames, String> currValues, Map<PropertyNames, String> parentValues) {
1083
1084         Either<Boolean, ResponseFormat> result;
1085         for (Entry<PropertyNames, String> entry : newValues.entrySet()) {
1086             PropertyNames currPropertyName = entry.getKey();
1087             if (currPropertyName == PropertyNames.MIN_INSTANCES) {
1088                 String minValue = parentValues.get(PropertyNames.MIN_INSTANCES);
1089                 String maxValue = newValues.containsKey(PropertyNames.INITIAL_COUNT) ? newValues.get(PropertyNames.MAX_INSTANCES) : currValues.get(PropertyNames.INITIAL_COUNT);
1090                 result = validateValueInRange(new ImmutablePair<PropertyNames, String>(currPropertyName, entry.getValue()), new ImmutablePair<PropertyNames, String>(PropertyNames.MIN_INSTANCES, minValue),
1091                         new ImmutablePair<PropertyNames, String>(PropertyNames.MAX_INSTANCES, maxValue));
1092                 if (result.isRight()) {
1093                     return result;
1094                 }
1095             } else if (currPropertyName == PropertyNames.INITIAL_COUNT) {
1096                 String minValue = newValues.containsKey(PropertyNames.MIN_INSTANCES) ? newValues.get(PropertyNames.MIN_INSTANCES) : currValues.get(PropertyNames.MIN_INSTANCES);
1097                 String maxValue = newValues.containsKey(PropertyNames.MAX_INSTANCES) ? newValues.get(PropertyNames.MAX_INSTANCES) : currValues.get(PropertyNames.MAX_INSTANCES);
1098                 result = validateValueInRange(new ImmutablePair<PropertyNames, String>(currPropertyName, entry.getValue()), new ImmutablePair<PropertyNames, String>(PropertyNames.MIN_INSTANCES, minValue),
1099                         new ImmutablePair<PropertyNames, String>(PropertyNames.MAX_INSTANCES, maxValue));
1100                 if (result.isRight()) {
1101                     return result;
1102                 }
1103             } else if (currPropertyName == PropertyNames.MAX_INSTANCES) {
1104                 String minValue = newValues.containsKey(PropertyNames.INITIAL_COUNT) ? newValues.get(PropertyNames.MIN_INSTANCES) : currValues.get(PropertyNames.INITIAL_COUNT);
1105                 String maxValue = parentValues.get(PropertyNames.MAX_INSTANCES);
1106                 result = validateValueInRange(new ImmutablePair<PropertyNames, String>(currPropertyName, entry.getValue()), new ImmutablePair<PropertyNames, String>(PropertyNames.MIN_INSTANCES, minValue),
1107                         new ImmutablePair<PropertyNames, String>(PropertyNames.MAX_INSTANCES, maxValue));
1108                 if (result.isRight()) {
1109                     return result;
1110                 }
1111             }
1112         }
1113         return Either.left(true);
1114     }
1115
1116     private Either<Boolean, ResponseFormat> validateValueInRange(ImmutablePair<PropertyNames, String> newValue, ImmutablePair<PropertyNames, String> min, ImmutablePair<PropertyNames, String> max) {
1117         Either<Boolean, ResponseFormat> result;
1118         final String warnMessage = "Failed to validate {} as property value of {}. It must be not higher than {}, and not lower than {}.";
1119         int newValueInt = parseIntValue(newValue.getValue(), newValue.getKey());
1120         int minInt = parseIntValue(min.getValue(), min.getKey());
1121         int maxInt = parseIntValue(max.getValue(), max.getKey());
1122         if (newValueInt < 0 || minInt < 0 || maxInt < 0) {
1123             result = Either.right(componentsUtils.getResponseFormat(ActionStatus.INVALID_PROPERTY));
1124         } else if (newValueInt < minInt || newValueInt > maxInt) {
1125             log.debug(warnMessage, newValue.getValue(), newValue.getKey().getPropertyName(), min.getValue(), max.getValue());
1126             result = Either
1127                     .right(componentsUtils.getResponseFormat(ActionStatus.INVALID_GROUP_MIN_MAX_INSTANCES_PROPERTY_VALUE, newValue.getKey().getPropertyName(), maxInt == Integer.MAX_VALUE ? Constants.UNBOUNDED : max.getValue(), min.getValue()));
1128         } else {
1129             result = Either.left(true);
1130         }
1131         return result;
1132     }
1133
1134     private int parseIntValue(String value, PropertyNames propertyName) {
1135         int result;
1136         if (propertyName == PropertyNames.MAX_INSTANCES) {
1137             result = convertIfUnboundMax(value);
1138         } else if (NumberUtils.isNumber(value)) {
1139             result = Integer.parseInt(value);
1140         } else {
1141             result = -1;
1142         }
1143         return result;
1144     }
1145
1146     /**
1147      * validates received new property values and updates group instance in case of success
1148      *
1149      * @param oldGroupInstance
1150      * @param newProperties
1151      * @param inTransaction
1152      * @return
1153      */
1154     public Either<GroupInstance, ResponseFormat> validateAndUpdateGroupInstancePropertyValues(String componentId, String instanceId, GroupInstance oldGroupInstance, List<GroupInstanceProperty> newProperties, boolean inTransaction) {
1155
1156         Either<GroupInstance, ResponseFormat> actionResult = null;
1157         Either<GroupInstance, StorageOperationStatus> updateGroupInstanceResult = null;
1158         Either<List<GroupInstanceProperty>, ResponseFormat> validateRes = validateReduceGroupInstancePropertiesBeforeUpdate(oldGroupInstance, newProperties);
1159         if (validateRes.isRight()) {
1160             log.debug("Failed to validate group instance {} properties before update. ", oldGroupInstance.getName());
1161             actionResult = Either.right(validateRes.right().value());
1162         }
1163         if (actionResult == null) {
1164             List<GroupInstanceProperty> validatedReducedNewProperties = validateRes.left().value();
1165             updateGroupInstanceResult = groupsOperation.updateGroupInstancePropertyValuesOnGraph(componentId, instanceId, oldGroupInstance, validatedReducedNewProperties);
1166             if (updateGroupInstanceResult.isRight()) {
1167                 log.debug("Failed to update group instance {} property values. ", oldGroupInstance.getName());
1168                 actionResult = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(updateGroupInstanceResult.right().value())));
1169             }
1170         }
1171         if (actionResult == null) {
1172             actionResult = Either.left(updateGroupInstanceResult.left().value());
1173         }
1174         return actionResult;
1175     }
1176
1177     private Either<List<GroupInstanceProperty>, ResponseFormat> validateReduceGroupInstancePropertiesBeforeUpdate(GroupInstance oldGroupInstance, List<GroupInstanceProperty> newProperties) {
1178
1179         Either<Boolean, ResponseFormat> validationRes = null;
1180         Either<List<GroupInstanceProperty>, ResponseFormat> actionResult;
1181         Map<String, GroupInstanceProperty> existingProperties = oldGroupInstance.convertToGroupInstancesProperties().stream().collect(Collectors.toMap(p -> p.getName(), p -> p));
1182         Map<PropertyNames, String> newPropertyValues = new EnumMap<>(PropertyNames.class);
1183         List<GroupInstanceProperty> reducedProperties = new ArrayList<>();
1184         String currPropertyName;
1185         try {
1186             for (GroupInstanceProperty currNewProperty : newProperties) {
1187                 currPropertyName = currNewProperty.getName();
1188                 validationRes = handleAndAddProperty(reducedProperties, newPropertyValues, currNewProperty, existingProperties.get(currPropertyName));
1189                 if (validationRes.isRight()) {
1190                     log.debug("Failed to handle property {} of group instance {}. ", currPropertyName, oldGroupInstance.getName());
1191                     break;
1192                 }
1193             }
1194             if (validationRes == null || validationRes.isLeft()) {
1195                 Map<PropertyNames, String> existingPropertyValues = new EnumMap<>(PropertyNames.class);
1196                 Map<PropertyNames, String> parentPropertyValues = new EnumMap<>(PropertyNames.class);
1197                 fillValuesAndParentValuesFromExistingProperties(existingProperties, existingPropertyValues, parentPropertyValues);
1198                 validationRes = validateMinMaxAndInitialCountPropertyLogic(newPropertyValues, existingPropertyValues, parentPropertyValues);
1199             }
1200             if (validationRes.isLeft()) {
1201                 actionResult = Either.left(reducedProperties);
1202             } else {
1203                 actionResult = Either.right(validationRes.right().value());
1204             }
1205         } catch (Exception e) {
1206             log.error("Exception occured during validation and reducing group instance properties. The message is {}", e.getMessage(), e);
1207             actionResult = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
1208         }
1209         return actionResult;
1210     }
1211
1212     private void fillValuesAndParentValuesFromExistingProperties(Map<String, GroupInstanceProperty> existingProperties, Map<PropertyNames, String> propertyValues, Map<PropertyNames, String> parentPropertyValues) {
1213         PropertyNames[] allPropertyNames = PropertyNames.values();
1214         for (PropertyNames name : allPropertyNames) {
1215             if (isUpdatable(name)) {
1216                 propertyValues.put(name, String.valueOf(existingProperties.get(name.getPropertyName()).getValue()));
1217                 parentPropertyValues.put(name, String.valueOf(existingProperties.get(name.getPropertyName()).getParentValue()));
1218             }
1219         }
1220     }
1221
1222     private Either<Boolean, ResponseFormat> handleAndAddProperty(List<GroupInstanceProperty> reducedProperties, Map<PropertyNames, String> newPropertyValues, GroupInstanceProperty currNewProperty, GroupInstanceProperty currExistingProperty) {
1223
1224         Either<Boolean, ResponseFormat> validationRes = null;
1225         String currPropertyName = currNewProperty.getName();
1226         PropertyNames propertyName = PropertyNames.findName(currPropertyName);
1227         try {
1228             if (currExistingProperty == null) {
1229                 log.warn("The value of property with the name {} cannot be updated. The property not found on group instance. ", currPropertyName);
1230             } else if (isUpdatable(propertyName)) {
1231                 validationRes = validateAndUpdatePropertyValue(currNewProperty, currExistingProperty);
1232                 if (validationRes.isRight()) {
1233                     log.debug("Failed to validate property value {} of property {}. ", currNewProperty.getValue(), currPropertyName);
1234                 } else {
1235                     addPropertyUpdatedValues(reducedProperties, propertyName, newPropertyValues, currNewProperty, currExistingProperty);
1236                 }
1237             } else {
1238                 validateImmutableProperty(currExistingProperty, currNewProperty);
1239             }
1240             if (validationRes == null) {
1241                 validationRes = Either.left(true);
1242             }
1243         } catch (Exception e) {
1244             log.error("Exception occured during handle and adding property. The message is {}", e.getMessage(), e);
1245             validationRes = Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
1246         }
1247         return validationRes;
1248     }
1249
1250     private boolean isUpdatable(PropertyNames updatablePropertyName) {
1251         return updatablePropertyName != null && updatablePropertyName.getUpdateBehavior().getLevelNumber() >= GroupInstancePropertyValueUpdateBehavior.UPDATABLE_ON_SERVICE_LEVEL.getLevelNumber();
1252     }
1253
1254     private void addPropertyUpdatedValues(List<GroupInstanceProperty> reducedProperties, PropertyNames propertyName, Map<PropertyNames, String> newPropertyValues, GroupInstanceProperty newProperty, GroupInstanceProperty existingProperty) {
1255
1256         String newValue = newProperty.getValue();
1257         if (!newValue.equals(String.valueOf(existingProperty.getValue()))) {
1258             newProperty.setValueUniqueUid(existingProperty.getValueUniqueUid());
1259             reducedProperties.add(newProperty);
1260         }
1261         if (!isEmptyMinInitialCountValue(propertyName, newValue)) {
1262             newPropertyValues.put(propertyName, newValue);
1263         }
1264     }
1265
1266     private boolean isEmptyMinInitialCountValue(PropertyNames propertyName, String newValue) {
1267         boolean result = false;
1268         if ((propertyName == PropertyNames.MIN_INSTANCES || propertyName == PropertyNames.INITIAL_COUNT) && !NumberUtils.isNumber(newValue)) {
1269             result = true;
1270         }
1271         return result;
1272     }
1273
1274     private int convertIfUnboundMax(String value) {
1275
1276         int result;
1277         if (!NumberUtils.isNumber(value)) {
1278             result = Integer.MAX_VALUE;
1279         } else {
1280             result = Integer.parseInt(value);
1281         }
1282         return result;
1283     }
1284
1285     private Either<Boolean, ResponseFormat> validateAndUpdatePropertyValue(GroupInstanceProperty newProperty, GroupInstanceProperty existingProperty) {
1286
1287         Either<Boolean, ResponseFormat> validationRes = null;
1288         String parentValue = existingProperty.getParentValue();
1289
1290         newProperty.setParentValue(parentValue);
1291         if (StringUtils.isEmpty(newProperty.getValue())) {
1292             newProperty.setValue(parentValue);
1293         }
1294         if (StringUtils.isEmpty(existingProperty.getValue())) {
1295             existingProperty.setValue(parentValue);
1296         }
1297         StorageOperationStatus status = groupOperation.validateAndUpdatePropertyValue(newProperty);
1298         if (status != StorageOperationStatus.OK) {
1299             log.debug("Failed to validate property value {} of property with name {}. Status is {}. ", newProperty.getValue(), newProperty.getName(), status);
1300             validationRes = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(status)));
1301         }
1302         if (validationRes == null) {
1303             validationRes = Either.left(true);
1304         }
1305         return validationRes;
1306     }
1307
1308     private void validateImmutableProperty(GroupProperty oldProperty, GroupProperty newProperty) {
1309         if (oldProperty.getValue() == null && newProperty.getValue() != null || oldProperty.getValue() != null && !oldProperty.getValue().equals(newProperty.getValue())) {
1310             log.warn("The value of property with the name {} cannot be updated on service level. Going to ignore new property value {}. ", oldProperty.getName(), newProperty.getValue());
1311         }
1312     }
1313
1314     public GroupDefinition createGroup(String groupType, ComponentTypeEnum componentTypeEnum, String componentId,
1315                                        String userId) {
1316
1317         try {
1318             Component component = accessValidations.validateUserCanWorkOnComponentAndLockIt(componentTypeEnum, componentId, userId, CREATE_GROUP);
1319
1320             validateGroupTypePerComponent(groupType, component);
1321
1322             GroupTypeDefinition groupTypeDefinition = groupTypeOperation.getLatestGroupTypeByType(groupType, false)
1323                     .left()
1324                     .on(se -> onGroupTypeNotFound(component));
1325
1326             boolean isFirstGroup = component.getGroups() == null;
1327             GroupDefinition groupDefinition = new GroupDefinition();
1328             groupDefinition.setType(groupType);
1329
1330             //find next valid counter
1331             int nextCounter = 0;
1332             if (!isFirstGroup) {
1333                 nextCounter = getNewGroupCounter(component);
1334             }
1335             String name = TopologyTemplateOperation.buildSubComponentName(component.getName(), groupType, nextCounter);
1336             groupDefinition.setName(name);
1337
1338             //Add default type properties
1339             List<PropertyDefinition> groupTypeProperties = groupTypeDefinition.getProperties();
1340             List<GroupProperty> properties = groupTypeProperties.stream()
1341                     .map(GroupProperty::new)
1342                     .collect(toList());
1343             groupDefinition.convertFromGroupProperties(properties);
1344
1345             List<GroupDefinition> gdList;
1346             if (isFirstGroup) {
1347                 gdList = createGroups(component, Arrays.asList(groupDefinition))
1348                         .left()
1349                         .on(this::onFailedGroupDBOperation);
1350             } else {
1351                 gdList = addGroups(component, Arrays.asList(groupDefinition))
1352                         .left()
1353                         .on(this::onFailedGroupDBOperation);
1354             }
1355             return gdList.get(0);
1356         } finally {
1357             titanDao.commit();
1358             graphLockOperation.unlockComponent(componentId, componentTypeEnum.getNodeType());
1359         }
1360     }
1361
1362     private void validateGroupTypePerComponent(String groupType, Component component) {
1363         String specificType = component.getComponentMetadataDefinition().getMetadataDataDefinition().getActualComponentType();
1364         if (!component.isTopologyTemplate()) {
1365             throw new ComponentException(ActionStatus.GROUP_TYPE_ILLEGAL_PER_COMPONENT, groupType,
1366                     specificType);
1367         }
1368         Map<String, Set<String>> excludedGroupTypesMap = ConfigurationManager.getConfigurationManager().getConfiguration()
1369                 .getExcludedGroupTypesMapping();
1370
1371         if (MapUtils.isNotEmpty(excludedGroupTypesMap) && StringUtils.isNotEmpty(specificType)) {
1372             Set<String> excludedGroupTypesPerComponent = excludedGroupTypesMap.get(specificType);
1373             if (excludedGroupTypesPerComponent!=null && excludedGroupTypesPerComponent.contains(groupType)) {
1374                 throw new ComponentException(ActionStatus.GROUP_TYPE_ILLEGAL_PER_COMPONENT, groupType, specificType);
1375             }
1376         }
1377     }
1378
1379     private int getNewGroupCounter(Component component) {
1380         List<String> existingNames = component.getGroups()
1381                 .stream()
1382                 .map(GroupDataDefinition::getName)
1383                 .collect(toList());
1384         List<String> existingIds = component.getGroups()
1385                 .stream()
1386                 .map(GroupDataDefinition::getUniqueId)
1387                 .collect(toList());
1388         existingIds.addAll(existingNames);
1389
1390         return Utils.getNextCounter(existingIds);
1391     }
1392
1393     public GroupDefinition updateGroup(ComponentTypeEnum componentTypeEnum, String componentId, String groupId,
1394                                        String userId, GroupDefinition updatedGroup) {
1395         try {
1396             Component component = accessValidations.validateUserCanWorkOnComponentAndLockIt(componentTypeEnum, componentId, userId, UPDATE_GROUP);
1397
1398             GroupDefinition existingGroup = findGroupOnComponent(component, groupId)
1399                     .left()
1400                     .on(se -> onGroupNotFoundInComponentError(component, groupId));
1401
1402             String existingGroupName = existingGroup.getName();
1403             String updatedGroupName = updatedGroup.getName();
1404             assertNewNameIsValidAndUnique(existingGroupName, updatedGroupName, component);
1405             existingGroup.setName(updatedGroupName);
1406
1407             return updateGroup(component, existingGroup, existingGroupName)
1408                     .left()
1409                     .on(this::onFailedUpdateGroupDBOperation);
1410         } finally {
1411             titanDao.commit();
1412             graphLockOperation.unlockComponent(componentId, componentTypeEnum.getNodeType());
1413         }
1414
1415     }
1416
1417     private void assertNewNameIsValidAndUnique(String currentGroupName, String updatedGroupName, Component component) {
1418         if (!ValidationUtils.validateResourceInstanceNameLength(updatedGroupName)) {
1419             throw new ComponentException(ActionStatus.EXCEEDS_LIMIT, "Group Name", ValidationUtils.RSI_NAME_MAX_LENGTH.toString());
1420         }
1421         if (!ValidationUtils.validateResourceInstanceName(updatedGroupName)) {
1422             throw new ComponentException(ActionStatus.INVALID_VF_MODULE_NAME, updatedGroupName);
1423         }
1424         if (!ComponentValidations.validateNameIsUniqueInComponent(currentGroupName, updatedGroupName, component)) {
1425             throw new ComponentException(ActionStatus.COMPONENT_NAME_ALREADY_EXIST, "Group", updatedGroupName);
1426         }
1427     }
1428
1429     public GroupDefinition deleteGroup(ComponentTypeEnum componentTypeEnum, String componentId, String groupId,
1430                                                                      String userId) {
1431         try {
1432             Component component = accessValidations.validateUserCanWorkOnComponentAndLockIt(componentTypeEnum, componentId, userId, DELETE_GROUP);
1433
1434             GroupDefinition groupDefinition = findGroupOnComponent(component, groupId)
1435                     .left()
1436                     .on(se -> onGroupNotFoundInComponentError(component, groupId));
1437
1438             List<GroupDefinition> gdList = deleteGroups(component, java.util.Arrays.asList(groupDefinition))
1439                     .left()
1440                     .on(this::onFailedGroupDBOperation);
1441             return gdList.get(0);
1442         } finally {
1443             titanDao.commit();
1444             graphLockOperation.unlockComponent(componentId, componentTypeEnum.getNodeType());
1445         }
1446     }
1447
1448     private List<GroupDefinition> onFailedGroupDBOperation(ResponseFormat responseFormat) {
1449         titanDao.rollback();
1450         throw new ComponentException(responseFormat);
1451     }
1452
1453     private GroupDefinition onFailedUpdateGroupDBOperation(ResponseFormat responseFormat) {
1454         titanDao.rollback();
1455         throw new ComponentException(responseFormat);
1456     }
1457
1458     private GroupDefinition onGroupNotFoundInComponentError(Component component, String groupId) {
1459         throw new ComponentException(ActionStatus.GROUP_IS_MISSING, groupId,
1460                 component.getSystemName(), getComponentTypeForResponse(component));
1461     }
1462
1463     private GroupTypeDefinition onGroupTypeNotFound(Component component) {
1464         throw new ComponentException(ActionStatus.GROUP_TYPE_IS_INVALID, component.getSystemName(),
1465                 component.getComponentType().toString());
1466     }
1467
1468     private Boolean onFailedToLockComponent(ResponseFormat responseFormat) {
1469         throw new ComponentException(responseFormat);
1470     }
1471
1472
1473     public Either<List<GroupDefinition>, ResponseFormat> createGroups(Component component, final List<GroupDefinition> groupDefinitions) {
1474
1475         Map<String, GroupDataDefinition> groups = new HashMap<>();
1476         Either<List<GroupDefinition>, ResponseFormat> result = null;
1477         Either<List<GroupDefinition>, StorageOperationStatus> createGroupsResult = null;
1478         Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = dataTypeCache.getAll();
1479         if (allDataTypes.isRight()) {
1480             TitanOperationStatus status = allDataTypes.right().value();
1481             BeEcompErrorManager.getInstance().logInternalFlowError("AddPropertyToGroup", "Failed to add property to group. Status is " + status, ErrorSeverity.ERROR);
1482             return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(DaoStatusConverter.convertTitanStatusToStorageStatus(status))));
1483
1484         }
1485
1486         // handle groups and convert to tosca data
1487         if (groupDefinitions != null && !groupDefinitions.isEmpty()) {
1488             for (GroupDefinition groupDefinition : groupDefinitions) {
1489                 Either<GroupDefinition, ResponseFormat> handleGroupRes = handleGroup(component, groupDefinition, allDataTypes.left().value());
1490                 if (handleGroupRes.isRight()) {
1491                     result = Either.right(handleGroupRes.right().value());
1492                     break;
1493                 }
1494                 GroupDefinition handledGroup = handleGroupRes.left().value();
1495                 groups.put(handledGroup.getName(), new GroupDataDefinition(handledGroup));
1496
1497             }
1498         }
1499         if (result == null) {
1500             createGroupsResult = groupsOperation.createGroups(component, groups);
1501             if (createGroupsResult.isRight()) {
1502                 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(createGroupsResult.right().value())));
1503             }
1504         }
1505         if (result == null) {
1506             result = Either.left(createGroupsResult.left().value());
1507         }
1508         return result;
1509     }
1510
1511     public Either<List<GroupDefinition>, ResponseFormat> addGroups(Component component, final List<GroupDefinition> groupDefinitions) {
1512
1513         Either<List<GroupDefinition>, ResponseFormat> result = null;
1514         Either<List<GroupDefinition>, StorageOperationStatus> createGroupsResult = null;
1515         List<GroupDataDefinition> groups = new ArrayList<>();
1516
1517         Either<Map<String, DataTypeDefinition>, TitanOperationStatus> allDataTypes = dataTypeCache.getAll();
1518         if (allDataTypes.isRight()) {
1519             TitanOperationStatus status = allDataTypes.right().value();
1520             BeEcompErrorManager.getInstance().logInternalFlowError("AddPropertyToGroup", "Failed to add property to group. Status is " + status, ErrorSeverity.ERROR);
1521             return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(DaoStatusConverter.convertTitanStatusToStorageStatus(status))));
1522
1523         }
1524
1525         // handle groups and convert to tosca data
1526         if (groupDefinitions != null && !groupDefinitions.isEmpty()) {
1527             for (GroupDefinition groupDefinition : groupDefinitions) {
1528                 Either<GroupDefinition, ResponseFormat> handleGroupRes = handleGroup(component, groupDefinition, allDataTypes.left().value());
1529                 if (handleGroupRes.isRight()) {
1530                     result = Either.right(handleGroupRes.right().value());
1531                     break;
1532                 }
1533                 GroupDefinition handledGroup = handleGroupRes.left().value();
1534                 groups.add(new GroupDataDefinition(handledGroup));
1535             }
1536         }
1537         if (result == null) {
1538             createGroupsResult = groupsOperation.addGroups(component, groups);
1539             if (createGroupsResult.isRight()) {
1540                 result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(createGroupsResult.right().value())));
1541             }
1542         }
1543         if (result == null) {
1544             result = Either.left(createGroupsResult.left().value());
1545         }
1546         return result;
1547     }
1548
1549     public Either<List<GroupDefinition>, ResponseFormat> deleteGroups(Component component, List<GroupDefinition> groupDefinitions) {
1550
1551         Either<List<GroupDefinition>, StorageOperationStatus> deleteGroupsResult;
1552
1553         deleteGroupsResult = groupsOperation.deleteGroups(component, groupDefinitions.stream().map(x -> new GroupDataDefinition(x)).collect(toList()));
1554         if (deleteGroupsResult.isRight()) {
1555             return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(deleteGroupsResult.right().value())));
1556         }
1557         return Either.left(deleteGroupsResult.left().value());
1558     }
1559
1560     /**
1561      * Update specific group version
1562      *
1563      */
1564     public Either<List<GroupDefinition>, ResponseFormat> updateGroups(Component component, List<GroupDefinition> groupDefinitions) {
1565
1566         Either<List<GroupDefinition>, ResponseFormat> result = null;
1567         Either<List<GroupDefinition>, StorageOperationStatus> createGroupsResult;
1568
1569         createGroupsResult = groupsOperation.updateGroups(component, groupDefinitions.stream().map(GroupDataDefinition::new).collect(toList()));
1570         if (createGroupsResult.isRight()) {
1571             result = Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(createGroupsResult.right().value())));
1572         }
1573
1574         if (result == null) {
1575             result = Either.left(createGroupsResult.left().value());
1576         }
1577         return result;
1578     }
1579
1580     public Either<GroupDefinition, ResponseFormat> handleGroup(Component component, GroupDefinition groupDefinition, Map<String, DataTypeDefinition> allDAtaTypes) {
1581
1582         log.trace("Going to create group {}", groupDefinition);
1583         // 3. verify group not already exist
1584         String groupDefinitionName = groupDefinition.getName();
1585         if (groupExistsInComponent(groupDefinitionName, component)) {
1586             String componentTypeForResponse = getComponentTypeForResponse(component);
1587             return Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_ALREADY_EXIST, groupDefinitionName, component.getNormalizedName(), componentTypeForResponse));
1588         }
1589         // 4. verify type of group exist
1590         String groupType = groupDefinition.getType();
1591         if (StringUtils.isEmpty(groupType)) {
1592             return Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_MISSING_GROUP_TYPE, groupDefinitionName));
1593         }
1594         Either<GroupTypeDefinition, StorageOperationStatus> getGroupType = groupTypeOperation.getLatestGroupTypeByType(groupType, true);
1595         if (getGroupType.isRight()) {
1596             StorageOperationStatus status = getGroupType.right().value();
1597             if (status == StorageOperationStatus.NOT_FOUND) {
1598                 BeEcompErrorManager.getInstance().logInvalidInputError(CREATE_GROUP, "group type " + groupType + " cannot be found", ErrorSeverity.INFO);
1599                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.GROUP_TYPE_IS_INVALID, groupType));
1600             } else {
1601                 return Either.right(componentsUtils.getResponseFormat(ActionStatus.GENERAL_ERROR));
1602             }
1603         }
1604         // 6. verify the component instances type are allowed according to
1605         // the member types in the group type
1606         GroupTypeDefinition groupTypeDefinition = getGroupType.left().value();
1607
1608         Either<Boolean, ResponseFormat> areValidMembers = verifyComponentInstancesAreValidMembers(component, groupDefinitionName, groupDefinition.getMembers(), groupTypeDefinition.getMembers());
1609
1610         if (areValidMembers.isRight()) {
1611             ResponseFormat responseFormat = areValidMembers.right().value();
1612             return Either.right(responseFormat);
1613         }
1614         // 7. verify the artifacts belongs to the component
1615         Either<Boolean, ResponseFormat> areValidArtifacts = verifyArtifactsBelongsToComponent(component, groupDefinition.getArtifacts(), CREATE_GROUP);
1616         if (areValidArtifacts.isRight()) {
1617             ResponseFormat responseFormat = areValidArtifacts.right().value();
1618             return Either.right(responseFormat);
1619         }
1620         List<PropertyDefinition> groupTypeProperties = groupTypeDefinition.getProperties();
1621
1622         List<GroupProperty> properties = groupDefinition.convertToGroupProperties();
1623         List<GroupProperty> updatedGroupTypeProperties = new ArrayList<>();
1624         if (CollectionUtils.isNotEmpty(properties)) {
1625             if (CollectionUtils.isEmpty(groupTypeProperties)) {
1626                 BeEcompErrorManager.getInstance().logInvalidInputError(ADDING_GROUP, "group type does not have properties", ErrorSeverity.INFO);
1627                 return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(DaoStatusConverter.convertTitanStatusToStorageStatus(TitanOperationStatus.MATCH_NOT_FOUND))));
1628             }
1629
1630             Map<String, PropertyDefinition> groupTypePropertiesMap = groupTypeProperties.stream().collect(Collectors.toMap(p -> p.getName(), p -> p));
1631
1632             Either<GroupProperty, TitanOperationStatus> addPropertyResult;
1633             int i = 1;
1634             for (GroupProperty prop : properties) {
1635                 addPropertyResult = handleProperty(prop, groupTypePropertiesMap.get(prop.getName()), i, allDAtaTypes, groupType);
1636                 if (addPropertyResult.isRight()) {
1637                     BeEcompErrorManager.getInstance().logInvalidInputError(ADDING_GROUP, "failed to validate property", ErrorSeverity.INFO);
1638                     return Either.right(componentsUtils.getResponseFormat(componentsUtils.convertFromStorageResponse(DaoStatusConverter.convertTitanStatusToStorageStatus(addPropertyResult.right().value()))));
1639                 }
1640                 updatedGroupTypeProperties.add(addPropertyResult.left().value());
1641
1642                 i++;
1643             }
1644         }
1645         if (groupDefinition.getUniqueId() == null) {
1646             String uid = UniqueIdBuilder.buildGroupingUid(component.getUniqueId(), groupDefinitionName);
1647             groupDefinition.setUniqueId(uid);
1648         }
1649         groupDefinition.convertFromGroupProperties(updatedGroupTypeProperties);
1650         groupDefinition.setInvariantUUID(UniqueIdBuilder.buildInvariantUUID());
1651         groupDefinition.setGroupUUID(UniqueIdBuilder.generateUUID());
1652         groupDefinition.setVersion(INITIAL_VERSION);
1653         groupDefinition.setTypeUid(groupTypeDefinition.getUniqueId());
1654
1655         return Either.left(groupDefinition);
1656     }
1657
1658     private static boolean groupExistsInComponent(String groupDefinitionName, Component component) {
1659         boolean found = false;
1660         List<GroupDefinition> groups = component.getGroups();
1661         if (CollectionUtils.isNotEmpty(groups)) {
1662             found = groups.stream().filter(p -> p.getName().equalsIgnoreCase(groupDefinitionName)).findFirst().orElse(null) != null;
1663         }
1664         return found;
1665     }
1666
1667     private Either<GroupProperty, TitanOperationStatus> handleProperty(GroupProperty groupProperty, PropertyDefinition prop, Integer index, Map<String, DataTypeDefinition> allDataTypes, String groupType) {
1668
1669         if (prop == null) {
1670             return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
1671         }
1672
1673         PropertyDataDefinition propDataDef = prop;
1674         String propertyType = propDataDef.getType();
1675         String value = groupProperty.getValue();
1676
1677         Either<String, TitanOperationStatus> checkInnerType = propertyOperation.checkInnerType(propDataDef);
1678         if (checkInnerType.isRight()) {
1679             TitanOperationStatus status = checkInnerType.right().value();
1680             return Either.right(status);
1681         }
1682
1683         String innerType = checkInnerType.left().value();
1684
1685         log.debug("Before validateAndUpdatePropertyValue");
1686         Either<Object, Boolean> isValid = propertyOperation.validateAndUpdatePropertyValue(propertyType, value, innerType, allDataTypes);
1687         log.debug("After validateAndUpdatePropertyValue. isValid = {}", isValid);
1688
1689         String newValue = value;
1690         if (isValid.isRight()) {
1691             Boolean res = isValid.right().value();
1692             if (!res) {
1693                 return Either.right(TitanOperationStatus.ILLEGAL_ARGUMENT);
1694             }
1695         } else {
1696             Object object = isValid.left().value();
1697             if (object != null) {
1698                 newValue = object.toString();
1699             }
1700         }
1701
1702         String uniqueId = shouldReconstructUniqueId(groupType) ? UniqueIdBuilder.buildGroupPropertyValueUid(prop.getUniqueId(), index)
1703                 : prop.getUniqueId();
1704
1705         groupProperty.setUniqueId(uniqueId);
1706         groupProperty.setValue(newValue);
1707         groupProperty.setType(prop.getType());
1708         groupProperty.setDefaultValue(prop.getDefaultValue());
1709         groupProperty.setDescription(prop.getDescription());
1710         groupProperty.setSchema(prop.getSchema());
1711         groupProperty.setPassword(prop.isPassword());
1712         groupProperty.setParentUniqueId(prop.getUniqueId());
1713
1714         log.debug("Before adding property value to graph {}", groupProperty);
1715
1716         return Either.left(groupProperty);
1717     }
1718
1719     // For old groups we want to leave indexing of property
1720     // For new groups we just need the types
1721     private boolean shouldReconstructUniqueId(String groupType) {
1722         return Constants.GROUP_TOSCA_HEAT.equals(groupType) || Constants.DEFAULT_GROUP_VF_MODULE.equals(groupType);
1723     }
1724
1725 }