re base code
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / jsontitan / operations / GroupsOperation.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.model.jsontitan.operations;
22
23 import fj.data.Either;
24 import org.apache.commons.collections.MapUtils;
25 import org.openecomp.sdc.be.dao.jsongraph.GraphVertex;
26 import org.openecomp.sdc.be.dao.jsongraph.types.EdgeLabelEnum;
27 import org.openecomp.sdc.be.dao.jsongraph.types.JsonParseFlagEnum;
28 import org.openecomp.sdc.be.dao.jsongraph.types.VertexTypeEnum;
29 import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
30 import org.openecomp.sdc.be.datatypes.elements.*;
31 import org.openecomp.sdc.be.datatypes.elements.MapCapabilityProperty;
32 import org.openecomp.sdc.be.datatypes.elements.MapListCapabilityDataDefinition;
33 import org.openecomp.sdc.be.datatypes.enums.JsonPresentationFields;
34 import org.openecomp.sdc.be.model.*;
35 import org.openecomp.sdc.be.model.jsontitan.utils.ModelConverter;
36 import org.openecomp.sdc.be.model.operations.StorageException;
37 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
38 import org.openecomp.sdc.be.model.operations.impl.DaoStatusConverter;
39 import org.openecomp.sdc.be.model.operations.impl.UniqueIdBuilder;
40 import org.openecomp.sdc.common.jsongraph.util.CommonUtility;
41 import org.openecomp.sdc.common.jsongraph.util.CommonUtility.LogLevelEnum;
42 import org.openecomp.sdc.common.log.wrappers.Logger;
43
44 import java.util.ArrayList;
45 import java.util.List;
46 import java.util.Map;
47 import java.util.Map.Entry;
48 import java.util.Optional;
49 import java.util.stream.Collectors;
50
51 @org.springframework.stereotype.Component("groups-operation")
52 public class GroupsOperation extends BaseOperation {
53
54     private static final Logger log = Logger.getLogger(GroupsOperation.class.getName());
55
56         public StorageOperationStatus deleteCalculatedCapabilitiesWithProperties(String componentId, List<GroupDefinition> groupDefinitions) {
57                 Optional<StorageOperationStatus> error = groupDefinitions.stream().map(g->removeCalculatedCapabilityFromComponent(componentId, g.getUniqueId())).filter(status-> status!=StorageOperationStatus.OK).findFirst();
58                 if(!error.isPresent()){
59                         Map<String, MapCapabilityProperty> extractCapabilityPropertiesFromGroups = ModelConverter.extractCapabilityPropertiesFromGroups(groupDefinitions, false);
60                         error = extractCapabilityPropertiesFromGroups.keySet().stream().map(k->removeCalculatedCapabilityPropertiesFromComponent(componentId, k)).filter(status-> status!=StorageOperationStatus.OK).findFirst();
61                 }
62                 if(error.isPresent()){
63                         return error.get();
64                 }
65                 return StorageOperationStatus.OK;
66         }
67
68         /**
69          * Adds the map of the calculated capabilities and the map of the calculated capabilities properties the the component on the graph
70          * @param       componentId
71          * @param       calculatedCapabilities
72          * @param       calculatedCapabilitiesProperties
73          * @return      status of the result the operation
74          */
75         public StorageOperationStatus addCalculatedCapabilitiesWithProperties(String componentId, Map<String, MapListCapabilityDataDefinition> calculatedCapabilities, Map<String, MapCapabilityProperty> calculatedCapabilitiesProperties) {
76
77                 Optional<StorageOperationStatus> error = calculatedCapabilities.entrySet().stream().map(e-> addElementToComponent(componentId, VertexTypeEnum.CALCULATED_CAPABILITIES, EdgeLabelEnum.CALCULATED_CAPABILITIES, e)).filter(status-> status!=StorageOperationStatus.OK).findFirst();
78                 if(!error.isPresent()){
79                         error = calculatedCapabilitiesProperties.entrySet().stream().map(e->addCalculatedCapabilityPropertiesToComponent(componentId, e)).filter(status-> status!=StorageOperationStatus.OK).findFirst();
80                 }
81                 if(error.isPresent()){
82                         return error.get();
83                 }
84                 return StorageOperationStatus.OK;
85         }
86         
87         public StorageOperationStatus updateCalculatedCapabilitiesWithProperties(String componentId, Map<String, MapListCapabilityDataDefinition> calculatedCapabilities, Map<String, MapCapabilityProperty> calculatedCapabilitiesProperties) {
88
89                 Optional<StorageOperationStatus> error = calculatedCapabilities.entrySet().stream().map(e->updateCalculatedCapabilityOfComponent(componentId, e)).filter(status-> status!=StorageOperationStatus.OK).findFirst();
90                 if(!error.isPresent()){
91                         error = calculatedCapabilitiesProperties.entrySet().stream().map(e->updateCalculatedCapabilityPropertiesOnComponent(componentId, e)).filter(status-> status!=StorageOperationStatus.OK).findFirst();
92                 }
93                 if(error.isPresent()){
94                         return error.get();
95                 }
96                 return StorageOperationStatus.OK;
97         }
98
99         private StorageOperationStatus updateCalculatedCapabilityOfComponent(String componentId, Entry<String, MapListCapabilityDataDefinition> capabilities){
100                 if(MapUtils.isNotEmpty(capabilities.getValue().getMapToscaDataDefinition()))
101                         return updateToscaDataDeepElementsBlockToToscaElement(componentId, EdgeLabelEnum.CALCULATED_CAPABILITIES, capabilities.getValue(), capabilities.getKey());
102                 return StorageOperationStatus.OK;
103         }
104         
105         private StorageOperationStatus addCalculatedCapabilityPropertiesToComponent(String componentId, Entry<String, MapCapabilityProperty> properties){
106                 if(MapUtils.isNotEmpty(properties.getValue().getMapToscaDataDefinition()))
107                         return addToscaDataDeepElementsBlockToToscaElement(componentId, EdgeLabelEnum.CALCULATED_CAP_PROPERTIES, VertexTypeEnum.CALCULATED_CAP_PROPERTIES, properties.getValue(), properties.getKey());
108                 return StorageOperationStatus.OK;
109         }
110         
111         private StorageOperationStatus updateCalculatedCapabilityPropertiesOnComponent(String componentId, Entry<String, MapCapabilityProperty> properties){
112                 if(MapUtils.isNotEmpty(properties.getValue().getMapToscaDataDefinition()))
113                         return updateToscaDataDeepElementsBlockToToscaElement(componentId, EdgeLabelEnum.CALCULATED_CAP_PROPERTIES, properties.getValue(), properties.getKey());
114                 return StorageOperationStatus.OK;
115         }
116         
117         private StorageOperationStatus removeCalculatedCapabilityFromComponent(String componentId, String groupId){
118                 return deleteToscaDataDeepElementsBlockOfToscaElement(componentId, EdgeLabelEnum.CALCULATED_CAPABILITIES, VertexTypeEnum.CALCULATED_CAPABILITIES, groupId);
119         }
120         
121         private StorageOperationStatus removeCalculatedCapabilityPropertiesFromComponent(String componentId, String groupId){
122                 return deleteToscaDataDeepElementsBlockOfToscaElement(componentId, EdgeLabelEnum.CALCULATED_CAP_PROPERTIES, VertexTypeEnum.CALCULATED_CAP_PROPERTIES, groupId);
123         }
124
125
126     public Either<List<GroupDefinition>, StorageOperationStatus> createGroups(Component component, Map<String, GroupDataDefinition> groups) {
127
128         Either<List<GroupDefinition>, StorageOperationStatus> result = null;
129         Either<GraphVertex, TitanOperationStatus> getComponentVertex = null;
130         StorageOperationStatus status = null;
131
132                 getComponentVertex = titanDao.getVertexById(component.getUniqueId(), JsonParseFlagEnum.NoParse);
133                 if (getComponentVertex.isRight()) {
134                         result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getComponentVertex.right().value()));
135                 }
136         if (result == null) {
137             status = topologyTemplateOperation.associateGroupsToComponent(getComponentVertex.left().value(), groups);
138             if (status != StorageOperationStatus.OK) {
139                 result = Either.right(status);
140             }
141         }
142         if (result == null) {
143             result = Either.left(ModelConverter.convertToGroupDefinitions(groups));
144         }
145         return result;
146     }
147
148     public <T extends GroupDataDefinition> Either<List<GroupDefinition>, StorageOperationStatus> addGroups(Component component, List<T> groups) {
149         Either<List<GroupDefinition>, StorageOperationStatus> result = null;
150         Either<GraphVertex, TitanOperationStatus> getComponentVertex;
151         StorageOperationStatus status;
152
153                 getComponentVertex = titanDao.getVertexById(component.getUniqueId(), JsonParseFlagEnum.NoParse);
154                 if (getComponentVertex.isRight()) {
155                         result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getComponentVertex.right().value()));
156                 }
157         if (result == null) {
158             status = addToscaDataToToscaElement(component.getUniqueId(), EdgeLabelEnum.GROUPS, VertexTypeEnum.GROUPS, groups, JsonPresentationFields.NAME);
159
160             if (status != StorageOperationStatus.OK) {
161                 result = Either.right(status);
162             }
163         }
164
165         if (result == null) {
166                         Map<String, GroupDataDefinition> mapGroup = groups.stream().collect(Collectors.toMap(GroupDataDefinition::getName, x->x));
167             result = Either.left(ModelConverter.convertToGroupDefinitions(mapGroup));
168         }
169         return result;
170     }
171
172     public Either<List<GroupDefinition>, StorageOperationStatus> deleteGroups(Component component, List<GroupDataDefinition> groups) {
173         Either<List<GroupDefinition>, StorageOperationStatus> result = null;
174         Either<GraphVertex, TitanOperationStatus> getComponentVertex = null;
175         StorageOperationStatus status = null;
176
177                 getComponentVertex = titanDao.getVertexById(component.getUniqueId(), JsonParseFlagEnum.NoParse);
178                 if (getComponentVertex.isRight()) {
179                         result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getComponentVertex.right().value()));
180                 }
181         if (result == null) {
182                         List<String> groupName = groups.stream().map(GroupDataDefinition::getName).collect(Collectors.toList());
183             status = deleteToscaDataElements(component.getUniqueId(), EdgeLabelEnum.GROUPS, groupName);
184
185             if (status != StorageOperationStatus.OK) {
186                 result = Either.right(status);
187             }
188         }
189
190         if (result == null) {
191                         Map<String, GroupDataDefinition> mapGroup = groups.stream().collect(Collectors.toMap( GroupDataDefinition::getName, x->x));
192             result = Either.left(ModelConverter.convertToGroupDefinitions(mapGroup));
193         }
194         return result;
195     }
196
197     public <T extends GroupDataDefinition> Either<List<GroupDefinition>, StorageOperationStatus> updateGroups(Component component, List<T> groups, boolean promoteVersion) {
198         Either<List<GroupDefinition>, StorageOperationStatus> result = null;
199         Either<GraphVertex, TitanOperationStatus> getComponentVertex = null;
200         StorageOperationStatus status = null;
201
202                 getComponentVertex = titanDao.getVertexById(component.getUniqueId(), JsonParseFlagEnum.NoParse);
203                 if (getComponentVertex.isRight()) {
204                         result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getComponentVertex.right().value()));
205                 }
206         if (result == null) {
207             groups.forEach(gr -> {
208                 updateVersion(promoteVersion, gr);
209                 String groupUUID = UniqueIdBuilder.generateUUID();
210                 gr.setGroupUUID(groupUUID);
211             });
212
213             status = updateToscaDataOfToscaElement(component.getUniqueId(), EdgeLabelEnum.GROUPS, VertexTypeEnum.GROUPS, groups, JsonPresentationFields.NAME);
214
215             if (status != StorageOperationStatus.OK) {
216                 result = Either.right(status);
217             }
218         }
219
220         if (result == null) {
221                         Map<String, GroupDataDefinition> mapGroup = groups.stream().collect(Collectors.toMap( GroupDataDefinition::getName, x->x));
222             result = Either.left(ModelConverter.convertToGroupDefinitions(mapGroup));
223         }
224         return result;
225     }
226
227     private <T extends GroupDataDefinition> void updateVersion(boolean promoteVersion, T group) {
228         if(promoteVersion) {
229             String version = group.getVersion();
230             String newVersion = increaseMajorVersion(version);
231             group.setVersion(newVersion);
232         }
233     }
234
235     public void updateGroupOnComponent(String componentId, GroupDefinition groupDefinition) {
236         GraphVertex componentVertex = titanDao.getVertexById(componentId, JsonParseFlagEnum.ParseMetadata)
237                 .left()
238                 .on(this::onTitanError);
239
240         StorageOperationStatus updateToscaResult = updateToscaDataOfToscaElement(componentVertex, EdgeLabelEnum.GROUPS, VertexTypeEnum.GROUPS, groupDefinition,
241                 JsonPresentationFields.NAME);
242
243         if (StorageOperationStatus.OK != updateToscaResult) {
244             throw new StorageException(updateToscaResult, groupDefinition.getUniqueId());
245         }
246
247         updateLastUpdateDate(componentVertex);
248     }
249
250     private void updateLastUpdateDate(GraphVertex componentVertex) {
251         componentVertex.setJsonMetadataField(JsonPresentationFields.LAST_UPDATE_DATE, System.currentTimeMillis());
252         titanDao.updateVertex(componentVertex)
253                 .left()
254                 .on(this::onTitanError);
255     }
256
257     GraphVertex onTitanError(TitanOperationStatus titanOperationStatus) {
258         throw new StorageException(
259                 DaoStatusConverter.convertTitanStatusToStorageStatus(titanOperationStatus));
260     }
261
262     public Either<List<GroupProperty>, StorageOperationStatus> updateGroupPropertiesOnComponent(String componentId, GroupDefinition group, List<GroupProperty> newGroupProperties) {
263
264         Either<List<GroupProperty>, StorageOperationStatus> result = null;
265         Either<GraphVertex, TitanOperationStatus> getComponentVertex = null;
266         GraphVertex componentVertex = null;
267
268                 getComponentVertex = titanDao.getVertexById(componentId, JsonParseFlagEnum.ParseMetadata);
269                 if (getComponentVertex.isRight()) {
270                         CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Failed to fetch component {}. Status is {} ", componentId);
271                         result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getComponentVertex.right().value()));
272                 }
273         if (result == null) {
274             componentVertex = getComponentVertex.left().value();
275             //update
276             List<PropertyDataDefinition> properties = group.getProperties();
277             newGroupProperties.forEach(np -> {
278                 Optional<PropertyDataDefinition> currentProp = properties.stream().filter(p -> p.getName().equals(np.getName())).findAny();
279                 if (currentProp.isPresent()) {
280                     currentProp.get().setValue(np.getValue());
281                 }
282             });
283
284             StorageOperationStatus updateDataRes = updateToscaDataOfToscaElement(componentVertex, EdgeLabelEnum.GROUPS, VertexTypeEnum.GROUPS, group, JsonPresentationFields.NAME);
285             if (updateDataRes != StorageOperationStatus.OK) {
286                 log.debug("Failed to update properties for group {} error {}", group.getName(), updateDataRes);
287                 result = Either.right(updateDataRes);
288             }
289         }
290         if (result == null) {
291             componentVertex.setJsonMetadataField(JsonPresentationFields.LAST_UPDATE_DATE, System.currentTimeMillis());
292             Either<GraphVertex, TitanOperationStatus> updateRes = titanDao.updateVertex(componentVertex);
293             if (updateRes.isRight()) {
294                 CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Failed to update the component {}. Status is {} ", componentId, updateRes.right().value());
295                 result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(updateRes.right().value()));
296             }
297         }
298         if (result == null) {
299             result = Either.left(newGroupProperties);
300         }
301         return result;
302     }
303
304     /**
305      * The version of the group is an integer. In order to support BC, we might get a version in a float format.
306      *
307      * @param version
308      * @return
309      */
310     private String increaseMajorVersion(String version) {
311
312         String[] versionParts = version.split(ToscaElementLifecycleOperation.VERSION_DELIMITER_REGEXP);
313         Integer majorVersion = Integer.parseInt(versionParts[0]);
314
315         majorVersion++;
316
317         return String.valueOf(majorVersion);
318
319     }
320
321     public Either<List<GroupInstance>, StorageOperationStatus> updateGroupInstances(Component component, String instanceId, List<GroupInstance> updatedGroupInstances) {
322
323         Either<List<GroupInstance>, StorageOperationStatus> result = null;
324         StorageOperationStatus status = null;
325
326         Either<GraphVertex, TitanOperationStatus> getComponentVertex = titanDao.getVertexById(component.getUniqueId(), JsonParseFlagEnum.NoParse);
327         if (getComponentVertex.isRight()) {
328             result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getComponentVertex.right().value()));
329         }
330         if (result == null) {
331             List<String> pathKeys = new ArrayList<>();
332             pathKeys.add(instanceId);
333             status = updateToscaDataDeepElementsOfToscaElement(component.getUniqueId(), EdgeLabelEnum.INST_GROUPS, VertexTypeEnum.INST_GROUPS, updatedGroupInstances, pathKeys, JsonPresentationFields.NAME);
334             if (status != StorageOperationStatus.OK) {
335                 result = Either.right(status);
336             }
337         }
338         if (result == null) {
339             result = Either.left(updatedGroupInstances);
340         }
341         return result;
342     }
343
344     public Either<GroupDefinition, StorageOperationStatus> updateGroup(Component component, GroupDefinition currentGroup) {
345         StorageOperationStatus status = updateToscaDataOfToscaElement(component.getUniqueId(), EdgeLabelEnum.GROUPS, VertexTypeEnum.GROUPS, currentGroup, JsonPresentationFields.NAME);
346         if (status != StorageOperationStatus.OK) {
347             CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Failed to update group {} of component {}. The status is}. ", currentGroup.getName(), component.getName(), status);
348             return Either.right(status);
349         }
350         return Either.left(currentGroup);
351     }
352
353     public StorageOperationStatus deleteGroup(Component component, String currentGroupName) {
354         StorageOperationStatus status = deleteToscaDataElement(component.getUniqueId(), EdgeLabelEnum.GROUPS, VertexTypeEnum.GROUPS, currentGroupName, JsonPresentationFields.NAME);
355         if (status != StorageOperationStatus.OK) {
356             CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Failed to delete group {} of component {}. The status is}. ", currentGroupName, component.getName(), status);
357         }
358         return status;
359     }
360
361     public Either<GroupDefinition, StorageOperationStatus> addGroup(Component component, GroupDefinition currentGroup) {
362         StorageOperationStatus status = addToscaDataToToscaElement(component.getUniqueId(), EdgeLabelEnum.GROUPS, VertexTypeEnum.GROUPS, currentGroup, JsonPresentationFields.NAME);
363         if (status != StorageOperationStatus.OK) {
364             CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Failed to update group {} of component {}. The status is}. ", currentGroup.getName(), component.getName(), status);
365             return Either.right(status);
366         }
367         return Either.left(currentGroup);
368     }
369
370     public Either<GroupInstance, StorageOperationStatus> updateGroupInstancePropertyValuesOnGraph(String componentId, String instanceId, GroupInstance oldGroupInstance, List<GroupInstanceProperty> newProperties) {
371
372         Either<GraphVertex, TitanOperationStatus> getComponentVertex = titanDao.getVertexById(componentId, JsonParseFlagEnum.ParseMetadata);
373         if (getComponentVertex.isRight()) {
374             CommonUtility.addRecordToLog(log, LogLevelEnum.DEBUG, "Failed to fetch component {}. Status is {} ", componentId);
375             return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(getComponentVertex.right().value()));
376         }
377
378         List<PropertyDataDefinition> propertiesOld = oldGroupInstance.getProperties();
379         newProperties.forEach(np -> {
380             Optional<PropertyDataDefinition> prop = propertiesOld.stream().filter(p -> p.getName().equals(np.getName())).findFirst();
381             if (prop.isPresent()) {
382                 prop.get().setValue(np.getValue());
383             }
384         });
385         GroupInstanceDataDefinition groupInstanceDataDefinition = new GroupInstanceDataDefinition(oldGroupInstance);
386         List<String> pathKeys = new ArrayList<>();
387         pathKeys.add(instanceId);
388         StorageOperationStatus updateDataRes = updateToscaDataDeepElementOfToscaElement(componentId, EdgeLabelEnum.INST_GROUPS, VertexTypeEnum.INST_GROUPS, groupInstanceDataDefinition, pathKeys, JsonPresentationFields.NAME);
389         if (updateDataRes != StorageOperationStatus.OK) {
390             log.debug("Failed to update properties for group instance {} error {}", oldGroupInstance.getName(), updateDataRes);
391             return Either.right(updateDataRes);
392         }
393         return Either.left(oldGroupInstance);
394     }
395 }