re base code
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / operations / impl / CapabilityTypeOperation.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.operations.impl;
22
23 import fj.data.Either;
24 import org.apache.commons.collections.MapUtils;
25 import org.apache.commons.lang3.StringUtils;
26 import org.apache.commons.lang3.tuple.ImmutablePair;
27 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
28 import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
29 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
30 import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
31 import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
32 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
33 import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
34 import org.openecomp.sdc.be.model.PropertyDefinition;
35 import org.openecomp.sdc.be.model.operations.api.DerivedFromOperation;
36 import org.openecomp.sdc.be.model.operations.api.ICapabilityTypeOperation;
37 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
38 import org.openecomp.sdc.be.model.operations.api.TypeOperations;
39 import org.openecomp.sdc.be.resources.data.CapabilityTypeData;
40 import org.openecomp.sdc.be.resources.data.PropertyData;
41 import org.openecomp.sdc.common.log.wrappers.Logger;
42 import org.springframework.beans.factory.annotation.Autowired;
43 import org.springframework.stereotype.Component;
44
45 import java.util.List;
46 import java.util.Map;
47 import java.util.stream.Collectors;
48
49 @Component("capability-type-operation")
50 public class CapabilityTypeOperation extends AbstractOperation implements ICapabilityTypeOperation {
51     @Autowired
52     private PropertyOperation propertyOperation;
53     @Autowired
54     private DerivedFromOperation derivedFromOperation;
55
56     public CapabilityTypeOperation() {
57         super();
58     }
59
60     private static final Logger log = Logger.getLogger(CapabilityTypeOperation.class.getName());
61
62     /**
63      * FOR TEST ONLY
64      *
65      * @param titanGenericDao
66      */
67     public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
68         this.titanGenericDao = titanGenericDao;
69     }
70
71     @Override
72     public Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType(CapabilityTypeDefinition capabilityTypeDefinition, boolean inTransaction) {
73
74         Either<CapabilityTypeDefinition, StorageOperationStatus> result = null;
75
76         try {
77             Either<CapabilityTypeDefinition, StorageOperationStatus> validationRes = validateUpdateProperties(capabilityTypeDefinition);
78             if (validationRes.isRight()) {
79                 log.error("#addCapabilityType - One or all properties of capability type {} not valid. status is {}", capabilityTypeDefinition, validationRes.right().value());
80                 return result;
81             }
82             
83             Either<CapabilityTypeData, StorageOperationStatus> eitherStatus = addCapabilityTypeToGraph(capabilityTypeDefinition);
84
85             result = eitherStatus.left()
86                         .map(CapabilityTypeData::getUniqueId)
87                         .left()
88                         .bind(uniqueId -> getCapabilityType(uniqueId, inTransaction));
89             
90             if(result.isLeft()) {
91                 log.debug("#addCapabilityType - The returned CapabilityTypeDefinition is {}", result.left().value());
92             }
93             
94             return result;
95         }
96
97         finally {
98             if (!inTransaction) {
99                 if (result == null || result.isRight()) {
100                     log.error("#addCapabilityType - Going to execute rollback on graph.");
101                     titanGenericDao.rollback();
102                 } else {
103                     log.debug("#addCapabilityType - Going to execute commit on graph.");
104                     titanGenericDao.commit();
105                 }
106             }
107         }
108
109     }
110     
111     public Either<Map<String, PropertyDefinition>, TitanOperationStatus> getAllCapabilityTypePropertiesFromAllDerivedFrom(String firstParentType) {
112         return propertyOperation.getAllTypePropertiesFromAllDerivedFrom(firstParentType, NodeTypeEnum.CapabilityType, CapabilityTypeData.class);
113     }
114
115     public Either<CapabilityTypeDefinition, StorageOperationStatus> validateUpdateProperties(CapabilityTypeDefinition capabilityTypeDefinition) {
116         TitanOperationStatus error = null;
117         if (MapUtils.isNotEmpty(capabilityTypeDefinition.getProperties()) && capabilityTypeDefinition.getDerivedFrom() != null) {
118             Either<Map<String, PropertyDefinition>, TitanOperationStatus> allPropertiesRes = 
119                                         getAllCapabilityTypePropertiesFromAllDerivedFrom(capabilityTypeDefinition.getDerivedFrom());
120             if (allPropertiesRes.isRight() && !allPropertiesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
121                 error = allPropertiesRes.right().value();
122                 log.debug("Couldn't fetch derived from property nodes for capability type {}, error: {}", capabilityTypeDefinition.getType(), error);
123             }
124             if (error == null && !allPropertiesRes.left().value().isEmpty()) {
125                 Map<String, PropertyDefinition> derivedFromProperties = allPropertiesRes.left().value();
126                 capabilityTypeDefinition.getProperties().entrySet().stream().filter(e -> derivedFromProperties.containsKey(e.getKey()) && e.getValue().getType() == null)
127                         .forEach(e -> e.getValue().setType(derivedFromProperties.get(e.getKey()).getType()));
128
129                 List<PropertyDefinition> properties = capabilityTypeDefinition.getProperties().values().stream().collect(Collectors.toList());
130                 Either<List<PropertyDefinition>, TitanOperationStatus> validatePropertiesRes = propertyOperation.validatePropertiesUniqueness(allPropertiesRes.left().value(),
131                         properties);
132                 if (validatePropertiesRes.isRight()) {
133                     error = validatePropertiesRes.right().value();
134                 }
135             }
136         }
137         if (error == null) {
138             return Either.left(capabilityTypeDefinition);
139         }
140         return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(error));
141     }
142     
143
144     /**
145      *
146      * convert between graph Node object to Java object
147      *
148      * @param capabilityTypeData
149      * @return
150      */
151     protected CapabilityTypeDefinition convertCTDataToCTDefinition(CapabilityTypeData capabilityTypeData) {
152         log.debug("The object returned after create capability is {}", capabilityTypeData);
153
154         return new CapabilityTypeDefinition(capabilityTypeData.getCapabilityTypeDataDefinition());
155     }
156
157     /**
158      *
159      * Add capability type to graph.
160      *
161      * 1. Add capability type node
162      *
163      * 2. Add edge between the former node to its parent(if exists)
164      *
165      * 3. Add property node and associate it to the node created at #1. (per property & if exists)
166      *
167      * @param capabilityTypeDefinition
168      * @return
169      */
170     private Either<CapabilityTypeData, StorageOperationStatus> addCapabilityTypeToGraph(CapabilityTypeDefinition capabilityTypeDefinition) {
171
172         log.debug("Got capability type {}", capabilityTypeDefinition);
173
174         String ctUniqueId = UniqueIdBuilder.buildCapabilityTypeUid(capabilityTypeDefinition.getType());
175         CapabilityTypeData capabilityTypeData = buildCapabilityTypeData(capabilityTypeDefinition, ctUniqueId);
176
177         log.debug("Before adding capability type to graph. capabilityTypeData = {}", capabilityTypeData);
178         Either<CapabilityTypeData, TitanOperationStatus> createCTResult = titanGenericDao.createNode(capabilityTypeData, CapabilityTypeData.class);
179         log.debug("After adding capability type to graph. status is = {}", createCTResult);
180
181         if (createCTResult.isRight()) {
182             TitanOperationStatus operationStatus = createCTResult.right().value();
183             log.error("Failed to capability type {} to graph. status is {}", capabilityTypeDefinition.getType(), operationStatus);
184             return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(operationStatus));
185         }
186
187         CapabilityTypeData resultCTD = createCTResult.left().value();
188         Map<String, PropertyDefinition> propertiesMap = capabilityTypeDefinition.getProperties();
189         Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesToCapablityType = propertyOperation.addPropertiesToElementType(resultCTD.getUniqueId(), NodeTypeEnum.CapabilityType, propertiesMap);
190         if (addPropertiesToCapablityType.isRight()) {
191             log.error("Failed add properties {} to capability {}", propertiesMap, capabilityTypeDefinition.getType());
192             return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(addPropertiesToCapablityType.right().value()));
193         }
194
195         return addDerivedFromRelation(capabilityTypeDefinition, ctUniqueId)
196                 .left()
197                 .map(updatedDerivedFrom -> createCTResult.left().value());
198
199
200     }
201
202     private CapabilityTypeData buildCapabilityTypeData(CapabilityTypeDefinition capabilityTypeDefinition, String ctUniqueId) {
203
204         CapabilityTypeData capabilityTypeData = new CapabilityTypeData(capabilityTypeDefinition);
205
206         capabilityTypeData.getCapabilityTypeDataDefinition().setUniqueId(ctUniqueId);
207         Long creationDate = capabilityTypeData.getCapabilityTypeDataDefinition().getCreationTime();
208         if (creationDate == null) {
209             creationDate = System.currentTimeMillis();
210         }
211         capabilityTypeData.getCapabilityTypeDataDefinition().setCreationTime(creationDate);
212         capabilityTypeData.getCapabilityTypeDataDefinition().setModificationTime(creationDate);
213         return capabilityTypeData;
214     }
215
216     @Override
217     public Either<CapabilityTypeDefinition, StorageOperationStatus> getCapabilityType(String uniqueId, boolean inTransaction) {
218
219         Either<CapabilityTypeDefinition, StorageOperationStatus> result = null;
220         try {
221
222             Either<CapabilityTypeDefinition, TitanOperationStatus> ctResult = this.getCapabilityTypeByUid(uniqueId);
223
224             if (ctResult.isRight()) {
225                 TitanOperationStatus status = ctResult.right().value();
226                 if (status != TitanOperationStatus.NOT_FOUND) {
227                     log.error("Failed to retrieve information on capability type {}. status is {}", uniqueId, status);
228                 }
229                 result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(ctResult.right().value()));
230                 return result;
231             }
232
233             result = Either.left(ctResult.left().value());
234
235             return result;
236         } finally {
237             if (!inTransaction) {
238                 log.debug("Going to execute commit on graph.");
239                 titanGenericDao.commit();
240             }
241         }
242     }
243
244
245     public Either<CapabilityTypeDefinition, TitanOperationStatus> getCapabilityTypeByType(String capabilityType) {
246         // Optimization: In case of Capability Type its unique ID is the same as type
247         return getCapabilityTypeByUid(capabilityType);
248     }
249
250     /**
251      * Build Capability type object from graph by unique id
252      *
253      * @param uniqueId
254      * @return
255      */
256     public Either<CapabilityTypeDefinition, TitanOperationStatus> getCapabilityTypeByUid(String uniqueId) {
257
258         Either<CapabilityTypeDefinition, TitanOperationStatus> result = null;
259
260         Either<CapabilityTypeData, TitanOperationStatus> capabilityTypesRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), uniqueId, CapabilityTypeData.class);
261
262         if (capabilityTypesRes.isRight()) {
263             TitanOperationStatus status = capabilityTypesRes.right().value();
264             log.debug("Capability type {} cannot be found in graph. status is {}", uniqueId, status);
265             return Either.right(status);
266         }
267
268         CapabilityTypeData ctData = capabilityTypesRes.left().value();
269         CapabilityTypeDefinition capabilityTypeDefinition = new CapabilityTypeDefinition(ctData.getCapabilityTypeDataDefinition());
270
271         TitanOperationStatus propertiesStatus = fillProperties(uniqueId, capabilityTypeDefinition);
272         if (propertiesStatus != TitanOperationStatus.OK) {
273             log.error("Failed to fetch properties of capability type {}", uniqueId);
274             return Either.right(propertiesStatus);
275         }
276
277         Either<ImmutablePair<CapabilityTypeData, GraphEdge>, TitanOperationStatus> parentNode = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), uniqueId, GraphEdgeLabels.DERIVED_FROM,
278                 NodeTypeEnum.CapabilityType, CapabilityTypeData.class);
279         log.debug("After retrieving DERIVED_FROM node of {}. status is {}", uniqueId, parentNode);
280         if (parentNode.isRight()) {
281             TitanOperationStatus titanOperationStatus = parentNode.right().value();
282             if (titanOperationStatus != TitanOperationStatus.NOT_FOUND) {
283                 log.error("Failed to find the parent capability of capability type {}. status is {}", uniqueId, titanOperationStatus);
284                 result = Either.right(titanOperationStatus);
285                 return result;
286             }
287         } else {
288             // derived from node was found
289             ImmutablePair<CapabilityTypeData, GraphEdge> immutablePair = parentNode.left().value();
290             CapabilityTypeData parentCT = immutablePair.getKey();
291             capabilityTypeDefinition.setDerivedFrom(parentCT.getCapabilityTypeDataDefinition().getType());
292         }
293         result = Either.left(capabilityTypeDefinition);
294
295         return result;
296     }
297
298     private TitanOperationStatus fillProperties(String uniqueId, CapabilityTypeDefinition capabilityTypeDefinition) {
299
300         Either<Map<String, PropertyDefinition>, TitanOperationStatus> findPropertiesOfNode = propertyOperation.findPropertiesOfNode(NodeTypeEnum.CapabilityType, uniqueId);
301         if (findPropertiesOfNode.isRight()) {
302             TitanOperationStatus titanOperationStatus = findPropertiesOfNode.right().value();
303             log.debug("After looking for properties of vertex {}. status is {}", uniqueId, titanOperationStatus);
304             if (TitanOperationStatus.NOT_FOUND.equals(titanOperationStatus)) {
305                 return TitanOperationStatus.OK;
306             } else {
307                 return titanOperationStatus;
308             }
309         } else {
310             Map<String, PropertyDefinition> properties = findPropertiesOfNode.left().value();
311             capabilityTypeDefinition.setProperties(properties);
312             return TitanOperationStatus.OK;
313         }
314     }
315
316     public Either<Boolean, StorageOperationStatus> isCapabilityTypeDerivedFrom(String childCandidateType, String parentCandidateType) {
317         return derivedFromOperation.isTypeDerivedFrom(childCandidateType, parentCandidateType, null, NodeTypeEnum.CapabilityType, CapabilityTypeData.class, t -> t.getCapabilityTypeDataDefinition().getType());
318     }
319     
320     
321     @Override
322     public Either<CapabilityTypeDefinition, StorageOperationStatus> updateCapabilityType(CapabilityTypeDefinition capabilityTypeDefNew, 
323                                                                                          CapabilityTypeDefinition capabilityTypeDefOld) {
324         log.debug("updating capability type {}", capabilityTypeDefNew.getType());
325         updateCapabilityTypeData(capabilityTypeDefNew, capabilityTypeDefOld);
326         return updateCapabilityTypeOnGraph(capabilityTypeDefNew, capabilityTypeDefOld);
327     }
328     
329     
330     private Either<CapabilityTypeDefinition, StorageOperationStatus> updateCapabilityTypeOnGraph(CapabilityTypeDefinition capabilityTypeDefinitionNew, CapabilityTypeDefinition capabilityTypeDefinitionOld) {
331         return titanGenericDao.updateNode(new CapabilityTypeData(capabilityTypeDefinitionNew), CapabilityTypeData.class)
332                 .right()
333                 .map(DaoStatusConverter::convertTitanStatusToStorageStatus)
334                 .left()
335                 .bind(updatedNode -> updateProperties(capabilityTypeDefinitionNew.getUniqueId(), capabilityTypeDefinitionNew.getProperties()))
336                 .left()
337                 .bind(updatedProperties -> updateDerivedFrom(capabilityTypeDefinitionNew, capabilityTypeDefinitionOld.getDerivedFrom()))
338                 .right()
339                 .bind(result -> TypeOperations.mapOkStatus(result, null))
340                 .left()
341                 .map(updatedDerivedFrom -> capabilityTypeDefinitionNew);
342     }
343
344     private Either<Map<String, PropertyData>, StorageOperationStatus> updateProperties(String capabilityTypeId, Map<String, PropertyDefinition> properties) {
345         log.debug("#updateCapabilityTypeProperties - updating properties for capability type with id {}", capabilityTypeId);
346         return propertyOperation.mergePropertiesAssociatedToNode(NodeTypeEnum.CapabilityType, capabilityTypeId, properties)
347                 .right()
348                 .map(DaoStatusConverter::convertTitanStatusToStorageStatus);
349     }
350
351     private Either<GraphRelation, StorageOperationStatus> updateDerivedFrom(CapabilityTypeDefinition updatedCapabilityType, String currDerivedFromCapabilityType) {
352         if( StringUtils.equals(updatedCapabilityType.getDerivedFrom(), currDerivedFromCapabilityType)) {
353             return Either.right(StorageOperationStatus.OK);
354         }
355         
356         StorageOperationStatus status = isLegalToReplaceParent(currDerivedFromCapabilityType, updatedCapabilityType.getDerivedFrom(), updatedCapabilityType.getType());
357         if ( status != StorageOperationStatus.OK) {
358             return Either.right(status);
359         }
360         
361         String capabilityTypeId = updatedCapabilityType.getUniqueId();
362         log.debug("#updateCapabilityTypeDerivedFrom - updating capability type derived from relation for capability type with id {}. old derived type {}. new derived type {}", capabilityTypeId, currDerivedFromCapabilityType, updatedCapabilityType.getDerivedFrom());
363         StorageOperationStatus deleteDerivedRelationStatus = deleteDerivedFromCapabilityType(capabilityTypeId, currDerivedFromCapabilityType);
364         if (deleteDerivedRelationStatus != StorageOperationStatus.OK) {
365             return Either.right(deleteDerivedRelationStatus);
366         }
367         return addDerivedFromRelation(updatedCapabilityType, capabilityTypeId);
368     }
369     
370     private StorageOperationStatus isLegalToReplaceParent(String oldTypeParent, String newTypeParent, String childType) {
371         return derivedFromOperation.isUpdateParentAllowed(oldTypeParent, newTypeParent, childType, NodeTypeEnum.CapabilityType, CapabilityTypeData.class, t -> t.getCapabilityTypeDataDefinition().getType());
372     }
373
374     private Either<GraphRelation, StorageOperationStatus> addDerivedFromRelation(CapabilityTypeDefinition capabilityTypeDef, String ptUniqueId) {
375         String derivedFrom = capabilityTypeDef.getDerivedFrom();
376         if (derivedFrom == null) {
377             return Either.left(null);
378         }
379         log.debug("#addDerivedFromRelationBefore - adding derived from relation between capability type {} to its parent {}", capabilityTypeDef.getType(), derivedFrom);
380         return this.getCapabilityType(derivedFrom, true)
381                 .left()
382                 .bind(derivedFromCapabilityType -> derivedFromOperation.addDerivedFromRelation(ptUniqueId, derivedFromCapabilityType.getUniqueId(), NodeTypeEnum.CapabilityType));
383     }
384
385     private StorageOperationStatus deleteDerivedFromCapabilityType(String capabilityTypeId, String derivedFromType) {
386         if (derivedFromType == null) {
387             return StorageOperationStatus.OK;
388         }
389         log.debug("#deleteDerivedFromCapabilityType - deleting derivedFrom relation for capability type with id {} and its derived type {}", capabilityTypeId, derivedFromType);
390         return getCapabilityType(derivedFromType, true)
391                 .either(derivedFromNode -> derivedFromOperation.removeDerivedFromRelation(capabilityTypeId, derivedFromNode.getUniqueId(), NodeTypeEnum.CapabilityType),
392                         err -> err);
393     }  
394     
395     private void updateCapabilityTypeData(CapabilityTypeDefinition updatedTypeDefinition, CapabilityTypeDefinition currTypeDefinition) {
396         updatedTypeDefinition.setUniqueId(currTypeDefinition.getUniqueId());
397         updatedTypeDefinition.setCreationTime(currTypeDefinition.getCreationTime());
398     }
399
400
401     /**
402      * FOR TEST ONLY
403      *
404      * @param propertyOperation
405      */
406     public void setPropertyOperation(PropertyOperation propertyOperation) {
407         this.propertyOperation = propertyOperation;
408     }
409
410     @Override
411     public Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType(CapabilityTypeDefinition capabilityTypeDefinition) {
412
413         return addCapabilityType(capabilityTypeDefinition, true);
414     }
415
416     @Override
417     public Either<CapabilityTypeDefinition, StorageOperationStatus> getCapabilityType(String uniqueId) {
418         return getCapabilityType(uniqueId, true);
419     }
420 }