Upgrade SDC from Titan to Janus Graph
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / operations / impl / GroupTypeOperation.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 com.google.common.base.Strings;
24 import org.janusgraph.graphdb.query.JanusGraphPredicate;
25 import fj.data.Either;
26 import org.apache.commons.collections.CollectionUtils;
27 import org.apache.commons.collections.MapUtils;
28 import org.apache.commons.lang.StringUtils;
29 import org.apache.commons.lang3.tuple.ImmutablePair;
30 import org.openecomp.sdc.be.config.BeEcompErrorManager;
31 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
32 import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
33 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphGenericDao;
34 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
35 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
36 import org.openecomp.sdc.be.dao.neo4j.GraphEdgePropertiesDictionary;
37 import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
38 import org.openecomp.sdc.be.datatypes.elements.GroupTypeDataDefinition;
39 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
40 import org.openecomp.sdc.be.model.CapabilityDefinition;
41 import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
42 import org.openecomp.sdc.be.model.GroupTypeDefinition;
43 import org.openecomp.sdc.be.model.PropertyDefinition;
44 import org.openecomp.sdc.be.model.operations.api.DerivedFromOperation;
45 import org.openecomp.sdc.be.model.operations.api.IGroupTypeOperation;
46 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
47 import org.openecomp.sdc.be.model.operations.api.TypeOperations;
48 import org.openecomp.sdc.be.model.utils.TypeCompareUtils;
49 import org.openecomp.sdc.be.resources.data.*;
50 import org.openecomp.sdc.common.log.wrappers.Logger;
51 import org.springframework.stereotype.Component;
52
53 import java.util.*;
54 import java.util.function.Function;
55 import java.util.stream.Collectors;
56
57 import static org.openecomp.sdc.be.dao.janusgraph.JanusGraphUtils.buildNotInPredicate;
58
59 @Component("group-type-operation")
60 public class GroupTypeOperation implements IGroupTypeOperation {
61
62     private static final Logger log = Logger.getLogger(GroupTypeOperation.class.getName());
63     private static final String CREATE_FLOW_CONTEXT = "CreateGroupType";
64
65     private final PropertyOperation propertyOperation;
66     private final JanusGraphGenericDao janusGraphGenericDao;
67     private final CapabilityTypeOperation capabilityTypeOperation;
68     private final CapabilityOperation capabilityOperation;
69     private final DerivedFromOperation derivedFromOperation;
70     private final OperationUtils operationUtils;
71
72
73     public GroupTypeOperation(JanusGraphGenericDao janusGraphGenericDao,
74                               PropertyOperation propertyOperation,
75                               CapabilityTypeOperation capabilityTypeOperation,
76                               CapabilityOperation capabilityOperation,
77                               DerivedFromOperation derivedFromOperation, OperationUtils operationUtils) {
78         this.janusGraphGenericDao = janusGraphGenericDao;
79         this.propertyOperation = propertyOperation;
80         this.capabilityTypeOperation = capabilityTypeOperation;
81         this.capabilityOperation = capabilityOperation;
82         this.derivedFromOperation = derivedFromOperation;
83         this.operationUtils = operationUtils;
84     }
85
86     @Override
87     public Either<GroupTypeDefinition, StorageOperationStatus> addGroupType(GroupTypeDefinition groupTypeDefinition) {
88         Either<GroupTypeDefinition, StorageOperationStatus> validationRes = validateUpdateProperties(groupTypeDefinition);
89         if (validationRes.isRight()) {
90             log.error("#addGroupType - One or all properties of group type {} not valid. status is {}", groupTypeDefinition, validationRes.right().value());
91             return validationRes;
92         }
93         
94         return addGroupType(groupTypeDefinition, true);
95     }
96
97     @Override
98     public Either<GroupTypeDefinition, StorageOperationStatus> addGroupType(GroupTypeDefinition groupTypeDefinition, boolean inTransaction) {
99
100         Either<GroupTypeDefinition, StorageOperationStatus> result = null;
101
102         try {
103
104             Either<GroupTypeData, JanusGraphOperationStatus> eitherStatus = addGroupTypeToGraph(groupTypeDefinition);
105
106             if (eitherStatus.isRight()) {
107                 BeEcompErrorManager.getInstance().logBeFailedCreateNodeError(CREATE_FLOW_CONTEXT, groupTypeDefinition.getType(), eitherStatus.right().value().name());
108                 result = Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(eitherStatus.right().value()));
109             }
110             else {
111                 result = getGroupType(eitherStatus.left().value().getUniqueId(), inTransaction);
112             }
113
114             return result;
115
116         } finally {
117             janusGraphGenericDao.handleTransactionCommitRollback(inTransaction, result);
118         }
119
120     }
121
122     @Override
123     public Either<GroupTypeDefinition, StorageOperationStatus> updateGroupType(GroupTypeDefinition updatedGroupType, GroupTypeDefinition currGroupType) {
124         log.debug("updating group type {}", updatedGroupType.getType());
125         return updateGroupTypeOnGraph(updatedGroupType, currGroupType);
126     }
127     
128     
129     public Either<GroupTypeDefinition, StorageOperationStatus> validateUpdateProperties(GroupTypeDefinition groupTypeDefinition) {
130         JanusGraphOperationStatus error = null;
131         if (CollectionUtils.isNotEmpty(groupTypeDefinition.getProperties()) && !Strings.isNullOrEmpty(groupTypeDefinition.getDerivedFrom())) {
132             Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> allPropertiesRes =
133                                         getAllGroupTypePropertiesFromAllDerivedFrom(groupTypeDefinition.getDerivedFrom());
134             if (allPropertiesRes.isRight() && !allPropertiesRes.right().value().equals(
135                 JanusGraphOperationStatus.NOT_FOUND)) {
136                 error = allPropertiesRes.right().value();
137                 log.debug("Couldn't fetch derived from property nodes for group type {}, error: {}", groupTypeDefinition.getType(), error);
138             }
139             if (error == null && !allPropertiesRes.left().value().isEmpty()) {
140                 Either<List<PropertyDefinition>, JanusGraphOperationStatus> validatePropertiesRes = propertyOperation.validatePropertiesUniqueness(allPropertiesRes.left().value(),
141                         groupTypeDefinition.getProperties());
142                 if (validatePropertiesRes.isRight()) {
143                     error = validatePropertiesRes.right().value();
144                 }
145             }
146         }
147         if (error == null) {
148             return Either.left(groupTypeDefinition);
149         }
150         return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(error));
151     }
152     
153     private Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> getAllGroupTypePropertiesFromAllDerivedFrom(String firstParentType) {
154         return janusGraphGenericDao
155             .getNode(GraphPropertiesDictionary.TYPE.getProperty(), firstParentType, GroupTypeData.class)
156                     .left()
157                     .bind(parentGroup -> propertyOperation.getAllTypePropertiesFromAllDerivedFrom(parentGroup.getUniqueId(), NodeTypeEnum.GroupType, GroupTypeData.class));
158     }
159
160
161     private StorageOperationStatus mergeCapabilities(GroupTypeDefinition groupTypeDef) {
162         Map<String, CapabilityDefinition> updatedGroupTypeCapabilities = groupTypeDef.getCapabilities();
163         Map<String, CapabilityDefinition> newGroupTypeCapabilities;
164         Either<List<CapabilityDefinition>, StorageOperationStatus> oldCapabilitiesRes = getCapablities(groupTypeDef.getUniqueId());
165         if (oldCapabilitiesRes.isRight()) {
166             StorageOperationStatus status = oldCapabilitiesRes.right().value();
167             if (status == StorageOperationStatus.NOT_FOUND) {
168                 newGroupTypeCapabilities = updatedGroupTypeCapabilities;
169             }
170             else {
171                 return status;
172             }
173         }
174         else {
175             Map<String, CapabilityDefinition> oldCapabilities = asCapabilitiesMap(oldCapabilitiesRes.left().value());
176             newGroupTypeCapabilities = collectNewCapabilities(updatedGroupTypeCapabilities, oldCapabilities);
177
178             for(Map.Entry<String, CapabilityDefinition> oldEntry: oldCapabilities.entrySet()) {
179                 String key = oldEntry.getKey();
180                 CapabilityDefinition newCapDef = updatedGroupTypeCapabilities != null? updatedGroupTypeCapabilities.get(key): null;
181                 CapabilityDefinition oldCapDef = oldEntry.getValue();
182
183                 StorageOperationStatus deleteCapResult = deleteOutdatedCapability(newGroupTypeCapabilities, newCapDef, oldCapDef);
184                 if(deleteCapResult != StorageOperationStatus.OK) {
185                     return deleteCapResult;
186                 }
187             }
188         }
189
190         JanusGraphOperationStatus createCapResult = createCapabilities(new GroupTypeData(groupTypeDef), newGroupTypeCapabilities);
191         return DaoStatusConverter.convertJanusGraphStatusToStorageStatus(createCapResult);
192     }
193
194     /**
195      * @param newGroupTypeCapabilities
196      * @param newCapDef
197      * @param oldCapDef
198      * @return 
199      */
200     private StorageOperationStatus deleteOutdatedCapability(Map<String, CapabilityDefinition> newGroupTypeCapabilities, CapabilityDefinition newCapDef, CapabilityDefinition oldCapDef) {
201         if(!isUpdateAllowed(newCapDef, oldCapDef)) {
202             return StorageOperationStatus.MATCH_NOT_FOUND;
203         }
204
205         if (!TypeCompareUtils.capabilityEquals(oldCapDef, newCapDef)) {
206             StorageOperationStatus deleteCapResult = capabilityOperation.deleteCapability(oldCapDef);
207
208             if(deleteCapResult == StorageOperationStatus.OK) {
209                 newGroupTypeCapabilities.put(newCapDef.getName(), newCapDef);
210             }
211             else {
212                 return deleteCapResult;
213             }
214         }
215         
216         return StorageOperationStatus.OK;
217     }
218
219     private boolean isUpdateAllowed(CapabilityDefinition newCapDef, CapabilityDefinition oldCapDef) {
220         if (newCapDef == null) {
221             log.error("#upsertCapabilities - Failed due to attempt to delete the capability with id {}", oldCapDef.getUniqueId());
222             return false;
223         }
224
225         if (newCapDef.getType() == null || !newCapDef.getType().equals(oldCapDef.getType())) {
226             log.error("#upsertCapabilities - Failed due to attempt to change type of the capability with id {}", oldCapDef.getUniqueId());
227             return false;
228         }
229         
230         return true;
231     }
232
233     /**
234      * @param updatedGroupTypeCapabilities
235      * @param oldCapabilities
236      * @return
237      */
238     private Map<String, CapabilityDefinition> collectNewCapabilities(Map<String, CapabilityDefinition> updatedGroupTypeCapabilities, Map<String, CapabilityDefinition> oldCapabilities) {
239         return updatedGroupTypeCapabilities != null? updatedGroupTypeCapabilities.entrySet().stream()
240                 .filter(entry -> !oldCapabilities.containsKey(entry.getKey()))
241                 .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue) ): null;
242     }
243
244     private JanusGraphOperationStatus createCapabilities(GroupTypeData groupTypeData, Map<String, CapabilityDefinition> groupCapabilities) {
245         if (MapUtils.isEmpty(groupCapabilities)) {
246             return JanusGraphOperationStatus.OK;
247         }
248         
249         return groupCapabilities.values().stream()
250                 .map(v -> createCapability(groupTypeData, v))
251                 .filter(Either::isRight)
252                 .findFirst()
253                 .map(either -> either.right().value())
254                 .orElse(JanusGraphOperationStatus.OK);
255     }
256
257     private Either<GraphRelation, JanusGraphOperationStatus> createCapability(GroupTypeData groupTypeData, CapabilityDefinition  capabilityDef) {
258         Either<CapabilityTypeDefinition, JanusGraphOperationStatus> eitherCapData = capabilityTypeOperation.getCapabilityTypeByType(capabilityDef.getType());
259         return eitherCapData
260                 .left()
261                 .map(CapabilityTypeData::new)
262                 .left()
263                 .bind(capTypeData -> capabilityOperation.addCapabilityToGraph(groupTypeData.getUniqueId(), capTypeData, capabilityDef))
264                 .left()
265                 .bind(capData -> connectToCapability(groupTypeData, capData, capabilityDef.getName()));
266     }
267
268
269     /**
270      * Get capability with all relevant properties
271      * @param groupTypeId
272      * @return
273      */
274     private Either<List<CapabilityDefinition>, StorageOperationStatus> getCapablities(String groupTypeId) {
275         Either<List<ImmutablePair<CapabilityData, GraphEdge>>, JanusGraphOperationStatus> groupCapabilitiesOnGraph =
276                 janusGraphGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.GroupType), groupTypeId, GraphEdgeLabels.GROUP_TYPE_CAPABILITY, NodeTypeEnum.Capability, CapabilityData.class, true);
277
278         if (groupCapabilitiesOnGraph.isRight()) {
279             JanusGraphOperationStatus capabilityStatus = groupCapabilitiesOnGraph.right().value();
280             if (capabilityStatus == JanusGraphOperationStatus.NOT_FOUND) {
281                 return Either.left(Collections.emptyList());
282             }
283             return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(capabilityStatus));
284         }
285
286         List<ImmutablePair<CapabilityData, GraphEdge>> groupCapabilites = groupCapabilitiesOnGraph.left().value();
287         groupCapabilites.forEach(this::fillCapabilityName);
288
289         return capabilityOperation.getCapabilitiesWithProps(groupCapabilites)
290                 .right()
291                 .map(DaoStatusConverter::convertJanusGraphStatusToStorageStatus);
292     }
293
294     private void fillCapabilityName(ImmutablePair<CapabilityData, GraphEdge> pair) {
295         pair.getLeft().getCapabilityDataDefinition().setName((String)pair.getRight().getProperties().get(GraphEdgePropertiesDictionary.NAME.getProperty()));
296     }
297
298     private Either<GraphRelation, JanusGraphOperationStatus> connectToCapability(GroupTypeData groupTypeData, CapabilityData capabilityData, String capabilityName) {
299         Map<String, Object> properties = new HashMap<>();
300         properties.put(GraphEdgePropertiesDictionary.NAME.getProperty(), capabilityName);
301
302         return janusGraphGenericDao.createRelation(groupTypeData, capabilityData, GraphEdgeLabels.GROUP_TYPE_CAPABILITY, properties);
303     }
304
305
306     public List<GroupTypeDefinition> getAllGroupTypes(Set<String> excludedGroupTypes) {
307         Map<String, Map.Entry<JanusGraphPredicate, Object>> predicateCriteria = buildNotInPredicate(GraphPropertiesDictionary.TYPE.getProperty(), excludedGroupTypes);
308         List<GroupTypeData> groupTypes = janusGraphGenericDao
309             .getByCriteriaWithPredicate(NodeTypeEnum.GroupType, predicateCriteria, GroupTypeData.class)
310                 .left()
311                 .on(operationUtils::onJanusGraphOperationFailure);
312         return convertGroupTypesToDefinition(groupTypes);
313     }
314
315
316     private List<GroupTypeDefinition> convertGroupTypesToDefinition(List<GroupTypeData> groupTypes) {
317         return groupTypes.stream()
318                 .map(type -> new GroupTypeDefinition(type.getGroupTypeDataDefinition()))
319                 .collect(Collectors.toList());
320     }
321
322
323     public Either<GroupTypeDefinition, StorageOperationStatus> getGroupTypeByUid(String uniqueId) {
324         log.debug("#getGroupTypeByUid - fetching group type with id {}", uniqueId);
325         return janusGraphGenericDao
326             .getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.GroupType), uniqueId, GroupTypeData.class)
327                 .right()
328                 .map(DaoStatusConverter::convertJanusGraphStatusToStorageStatus)
329                 .left()
330                 .bind(groupType -> buildGroupTypeDefinition(uniqueId, groupType));
331     }
332
333     @Override
334     public Either<GroupTypeDefinition, StorageOperationStatus> getGroupType(String uniqueId, boolean inTransaction) {
335         Either<GroupTypeDefinition, StorageOperationStatus> result = null;
336         try {
337
338             Either<GroupTypeDefinition, StorageOperationStatus> ctResult = getGroupTypeByUid(uniqueId);
339
340             if (ctResult.isRight()) {
341                 StorageOperationStatus status = ctResult.right().value();
342                 if (status != StorageOperationStatus.NOT_FOUND) {
343                     log.error("Failed to retrieve information on element uniqueId: {}. status is {}", uniqueId, status);
344                 }
345                 result = Either.right(ctResult.right().value());
346                 return result;
347             }
348
349             result = Either.left(ctResult.left().value());
350
351             return result;
352         } finally {
353             janusGraphGenericDao.handleTransactionCommitRollback(inTransaction, result);
354         }
355
356     }
357
358     @Override
359     public Either<GroupTypeDefinition, StorageOperationStatus> getLatestGroupTypeByType(String type) {
360         return getLatestGroupTypeByType(type, true);
361     }
362
363     @Override
364     public Either<GroupTypeDefinition, StorageOperationStatus> getLatestGroupTypeByType(String type, boolean inTransaction) {
365         Map<String, Object> mapCriteria = new HashMap<>();
366         mapCriteria.put(GraphPropertiesDictionary.TYPE.getProperty(), type);
367         mapCriteria.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
368
369         return getGroupTypeByCriteria(type, mapCriteria, inTransaction);
370
371     }
372
373     public Either<GroupTypeDefinition, StorageOperationStatus> getGroupTypeByCriteria(String type, Map<String, Object> properties, boolean inTransaction) {
374         Either<GroupTypeDefinition, StorageOperationStatus> result = null;
375         try {
376             if (type == null || type.isEmpty()) {
377                 log.error("type is empty");
378                 result = Either.right(StorageOperationStatus.INVALID_ID);
379                 return result;
380             }
381
382             Either<List<GroupTypeData>, StorageOperationStatus> groupTypeEither = janusGraphGenericDao
383                 .getByCriteria(NodeTypeEnum.GroupType, properties, GroupTypeData.class)
384                     .right()
385                     .map(DaoStatusConverter::convertJanusGraphStatusToStorageStatus);
386             if (groupTypeEither.isRight()) {
387                 result = Either.right(groupTypeEither.right().value());
388             } else {
389                 GroupTypeDataDefinition dataDefinition = groupTypeEither.left().value().stream()
390                         .map(GroupTypeData::getGroupTypeDataDefinition)
391                         .findFirst()
392                         .get();
393                 result = getGroupTypeByUid(dataDefinition.getUniqueId());
394
395             }
396             return result;
397
398         } finally {
399             janusGraphGenericDao.handleTransactionCommitRollback(inTransaction, result);
400         }
401     }
402
403     private Either<GroupTypeDefinition, StorageOperationStatus> buildGroupTypeDefinition(String uniqueId, GroupTypeData groupTypeNode) {
404         GroupTypeDefinition groupType = new GroupTypeDefinition(groupTypeNode.getGroupTypeDataDefinition());
405         return fillDerivedFrom(uniqueId, groupType)
406                 .left()
407                 .map(derivedFrom -> fillProperties(uniqueId, groupType, derivedFrom))
408                 .left()
409                 .bind(props -> fillCapabilities(uniqueId, groupType));
410     }
411     
412     private Either<GroupTypeDefinition, StorageOperationStatus> fillCapabilities(String uniqueId, GroupTypeDefinition groupType) {
413         return getCapablities(uniqueId)
414                 .left()
415                 .map(capabilities -> {
416                     groupType.setCapabilities(asCapabilitiesMap(capabilities));
417                     return groupType;
418                 });
419     }
420
421     private Either<GroupTypeData, StorageOperationStatus> fillDerivedFrom(String uniqueId, GroupTypeDefinition groupType) {
422         log.debug("#fillDerivedFrom - fetching group type {} derived node", groupType.getType());
423         return derivedFromOperation.getDerivedFromChild(uniqueId, NodeTypeEnum.GroupType, GroupTypeData.class)
424                 .right()
425                 .bind(this::handleDerivedFromNotExist)
426                 .left()
427                 .map(derivedFrom -> setDerivedFrom(groupType, derivedFrom));
428
429     }
430
431     private Either<List<PropertyDefinition>, StorageOperationStatus> fillProperties(String uniqueId, GroupTypeDefinition groupType, GroupTypeData derivedFromNode) {
432         log.debug("#fillProperties - fetching all properties for group type {}", groupType.getType());
433         return propertyOperation.findPropertiesOfNode(NodeTypeEnum.GroupType, uniqueId)
434                 .right()
435                 .bind(this::handleGroupTypeHasNoProperties)
436                 .left()
437                 .bind(propsMap -> fillDerivedFromProperties(groupType, derivedFromNode, new ArrayList<>(propsMap.values())));
438     }
439
440     Either<Map<String, PropertyDefinition>, StorageOperationStatus> handleGroupTypeHasNoProperties(JanusGraphOperationStatus err) {
441         if (err == JanusGraphOperationStatus.NOT_FOUND) {
442             return Either.left(new HashMap<>());
443         }
444         return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(err));
445     }
446
447     private Either<List<PropertyDefinition>, StorageOperationStatus> fillDerivedFromProperties(GroupTypeDefinition groupType, GroupTypeData derivedFromNode, List<PropertyDefinition> groupTypeDirectProperties) {
448         if (derivedFromNode == null) {
449             groupType.setProperties(groupTypeDirectProperties);
450             return Either.left(groupTypeDirectProperties);
451         }
452         log.debug("#fillDerivedFromProperties - fetching all properties of derived from chain for group type {}", groupType.getType());
453         return propertyOperation.getAllPropertiesRec(derivedFromNode.getUniqueId(), NodeTypeEnum.GroupType, GroupTypeData.class)
454                 .left()
455                 .map(derivedFromProps -> {groupTypeDirectProperties.addAll(derivedFromProps); return groupTypeDirectProperties;})
456                 .left()
457                 .map(allProps -> {groupType.setProperties(allProps);return allProps;});
458     }
459
460     private Either<GroupTypeData, StorageOperationStatus> handleDerivedFromNotExist(StorageOperationStatus err) {
461         if (err == StorageOperationStatus.NOT_FOUND) {
462             return Either.left(null);
463         }
464         return Either.right(err);
465     }
466
467     private GroupTypeData setDerivedFrom(GroupTypeDefinition groupTypeDefinition, GroupTypeData derivedFrom) {
468         if (derivedFrom != null) {
469             groupTypeDefinition.setDerivedFrom(derivedFrom.getGroupTypeDataDefinition().getType());
470         }
471         return derivedFrom;
472     }
473
474
475     @Override
476     public Either<GroupTypeDefinition, StorageOperationStatus> getGroupTypeByTypeAndVersion(String type, String version) {
477         return getGroupTypeByTypeAndVersion(type, version, false);
478     }
479
480     @Override
481     public Either<GroupTypeDefinition, StorageOperationStatus> getGroupTypeByTypeAndVersion(String type, String version, boolean inTransaction) {
482         Map<String, Object> mapCriteria = new HashMap<>();
483         mapCriteria.put(GraphPropertiesDictionary.TYPE.getProperty(), type);
484         mapCriteria.put(GraphPropertiesDictionary.VERSION.getProperty(), version);
485
486         return getGroupTypeByCriteria(type, mapCriteria, inTransaction);
487     }
488
489     /**
490      * Add group type to graph.
491      * <p>
492      * 1. Add group type node
493      * <p>
494      * 2. Add edge between the former node to its parent(if exists)
495      * <p>
496      * 3. Add property node and associate it to the node created at #1. (per property & if exists)
497      *
498      * @param groupTypeDefinition
499      * @return
500      */
501     private Either<GroupTypeData, JanusGraphOperationStatus> addGroupTypeToGraph(GroupTypeDefinition groupTypeDefinition) {
502
503         log.debug("Got group type {}", groupTypeDefinition);
504
505         String ctUniqueId = UniqueIdBuilder.buildGroupTypeUid(groupTypeDefinition.getType(), groupTypeDefinition.getVersion(), "grouptype");
506
507         GroupTypeData groupTypeData = buildGroupTypeData(groupTypeDefinition, ctUniqueId);
508
509         log.debug("Before adding group type to graph. groupTypeData = {}", groupTypeData);
510
511         Either<GroupTypeData, JanusGraphOperationStatus> createGTResult = janusGraphGenericDao
512             .createNode(groupTypeData, GroupTypeData.class);
513         log.debug("After adding group type to graph. status is = {}", createGTResult);
514
515         if (createGTResult.isRight()) {
516             JanusGraphOperationStatus operationStatus = createGTResult.right().value();
517             log.error("Failed to add group type {} to graph. status is {}", groupTypeDefinition.getType(), operationStatus);
518             return Either.right(operationStatus);
519         }
520
521         GroupTypeData resultCTD = createGTResult.left().value();
522         List<PropertyDefinition> properties = groupTypeDefinition.getProperties();
523         Either<Map<String, PropertyData>, JanusGraphOperationStatus> addPropertiesToCapablityType = propertyOperation.addPropertiesToElementType(resultCTD.getUniqueId(), NodeTypeEnum.GroupType, properties);
524         if (addPropertiesToCapablityType.isRight()) {
525             log.error("Failed add properties {} to capability {}", properties, groupTypeDefinition.getType());
526             return Either.right(addPropertiesToCapablityType.right().value());
527         }
528
529         String derivedFrom = groupTypeDefinition.getDerivedFrom();
530         if (derivedFrom != null) {
531             Either<GraphRelation, JanusGraphOperationStatus> createRelation = connectToDerivedFrom(ctUniqueId, derivedFrom);
532             if (createRelation.isRight()) {
533                 return Either.right(createRelation.right().value());
534             }
535         }
536         
537         Map<String, CapabilityDefinition> groupCapTypes = groupTypeDefinition.getCapabilities();
538         if (!MapUtils.isEmpty(groupCapTypes)) {
539             JanusGraphOperationStatus status = createCapabilities(groupTypeData, groupCapTypes);
540             if (status != JanusGraphOperationStatus.OK) {
541                 return Either.right(status);
542             }
543         }
544
545         return Either.left(createGTResult.left().value());
546
547     }
548
549
550     private Either<GraphRelation, JanusGraphOperationStatus> connectToDerivedFrom(String ctUniqueId, String derivedFrom) {
551         log.debug("Before creating relation between Group Type with id {} to its parent {}", ctUniqueId, derivedFrom);
552
553         Either<GroupTypeData, JanusGraphOperationStatus> derivedFromGroupTypeResult =
554                 janusGraphGenericDao
555                     .getNode(GraphPropertiesDictionary.TYPE.getProperty(), derivedFrom, GroupTypeData.class);
556
557         if (derivedFromGroupTypeResult.isLeft()) {
558             UniqueIdData from = new UniqueIdData(NodeTypeEnum.GroupType, ctUniqueId);
559             GroupTypeData to = derivedFromGroupTypeResult.left().value();
560
561             Either<GraphRelation, JanusGraphOperationStatus> createRelation = janusGraphGenericDao
562                 .createRelation(from, to, GraphEdgeLabels.DERIVED_FROM, null);
563             log.debug("After create relation between Group Type with id {} to its parent {}, status is {}.", ctUniqueId, derivedFrom, createRelation);
564             return createRelation;
565         } else {
566             JanusGraphOperationStatus status = derivedFromGroupTypeResult.right().value();
567             log.debug("Failed to found parent Group Type {}, stauts is {}.", derivedFrom, status);
568             return Either.right(status);
569         }
570     }
571
572     private GroupTypeData buildGroupTypeData(GroupTypeDefinition groupTypeDefinition, String ctUniqueId) {
573
574         GroupTypeData groupTypeData = new GroupTypeData(groupTypeDefinition);
575
576         groupTypeData.getGroupTypeDataDefinition().setUniqueId(ctUniqueId);
577         Long creationDate = groupTypeData.getGroupTypeDataDefinition().getCreationTime();
578         if (creationDate == null) {
579             creationDate = System.currentTimeMillis();
580         }
581         groupTypeData.getGroupTypeDataDefinition().setCreationTime(creationDate);
582         groupTypeData.getGroupTypeDataDefinition().setModificationTime(creationDate);
583
584         return groupTypeData;
585     }
586
587     public Either<Boolean, StorageOperationStatus> isCapabilityTypeDerivedFrom(String childCandidateType, String parentCandidateType) {
588         Map<String, Object> propertiesToMatch = new HashMap<>();
589         propertiesToMatch.put(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), childCandidateType);
590         Either<List<CapabilityTypeData>, JanusGraphOperationStatus> getResponse = janusGraphGenericDao
591             .getByCriteria(NodeTypeEnum.CapabilityType, propertiesToMatch, CapabilityTypeData.class);
592         if (getResponse.isRight()) {
593             JanusGraphOperationStatus janusGraphOperationStatus = getResponse.right().value();
594             log.debug("Couldn't fetch capability type {}, error: {}", childCandidateType,
595                 janusGraphOperationStatus);
596             return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(
597                 janusGraphOperationStatus));
598         }
599         String childUniqueId = getResponse.left().value().get(0).getUniqueId();
600         Set<String> travelledTypes = new HashSet<>();
601         do {
602             travelledTypes.add(childUniqueId);
603             Either<List<ImmutablePair<CapabilityTypeData, GraphEdge>>, JanusGraphOperationStatus> childrenNodes = janusGraphGenericDao
604                 .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), childUniqueId, GraphEdgeLabels.DERIVED_FROM,
605                     NodeTypeEnum.CapabilityType, CapabilityTypeData.class);
606             if (childrenNodes.isRight()) {
607                 if (childrenNodes.right().value() != JanusGraphOperationStatus.NOT_FOUND) {
608                     JanusGraphOperationStatus janusGraphOperationStatus = getResponse.right().value();
609                     log.debug("Couldn't fetch derived from node for capability type {}, error: {}", childCandidateType,
610                         janusGraphOperationStatus);
611                     return Either.right(DaoStatusConverter.convertJanusGraphStatusToStorageStatus(
612                         janusGraphOperationStatus));
613                 } else {
614                     log.debug("Derived from node is not found for type {} - this is OK for root capability.", childCandidateType);
615                     return Either.left(false);
616                 }
617             }
618             String derivedFromUniqueId = childrenNodes.left().value().get(0).getLeft().getUniqueId();
619             if (derivedFromUniqueId.equals(parentCandidateType)) {
620                 log.debug("Verified that capability type {} derives from capability type {}", childCandidateType, parentCandidateType);
621                 return Either.left(true);
622             }
623             childUniqueId = derivedFromUniqueId;
624         } while (!travelledTypes.contains(childUniqueId));
625         // this stop condition should never be used, if we use it, we have an
626         // illegal cycle in graph - "derived from" hierarchy cannot be cycled.
627         // It's here just to avoid infinite loop in case we have such cycle.
628         log.error("Detected a cycle of \"derived from\" edges starting at capability type node {}", childUniqueId);
629         return Either.right(StorageOperationStatus.GENERAL_ERROR);
630     }
631     
632     /**
633      * @param list
634      * @return
635      */
636     private Map<String, CapabilityDefinition> asCapabilitiesMap(List<CapabilityDefinition> list) {
637         return list.stream()
638                 .collect(Collectors.toMap(CapabilityDefinition::getName, Function.identity()));
639     }
640
641
642     private Either<GroupTypeDefinition, StorageOperationStatus> updateGroupTypeOnGraph(GroupTypeDefinition updatedGroupType, GroupTypeDefinition currGroupType) {
643         updateGroupTypeData(updatedGroupType, currGroupType);
644         return janusGraphGenericDao.updateNode(new GroupTypeData(updatedGroupType), GroupTypeData.class)
645                 .right()
646                 .map(DaoStatusConverter::convertJanusGraphStatusToStorageStatus)
647                 .left()
648                 .bind(updatedNode -> updateGroupProperties(updatedGroupType.getUniqueId(), updatedGroupType.getProperties()))
649                 .left()
650                 .bind(updatedProperties -> updateGroupDerivedFrom(updatedGroupType, currGroupType.getDerivedFrom()))
651                 .right()
652                 .bind(result -> TypeOperations.mapOkStatus(result, null))
653                 .left()
654                 .bind(updatedDerivedFrom -> TypeOperations.mapOkStatus(mergeCapabilities(updatedGroupType), updatedGroupType))
655                 .left()
656                 .bind(def -> getGroupTypeByUid(def.getUniqueId()));
657     }
658     
659
660     private Either<Map<String, PropertyData>, StorageOperationStatus> updateGroupProperties(String groupId, List<PropertyDefinition> properties) {
661         log.debug("#updateGroupProperties - updating group type properties for group type with id {}", groupId);
662         Map<String, PropertyDefinition> mapProperties = properties != null? properties.stream()
663                 .collect(Collectors.toMap(PropertyDefinition::getName, Function.identity())): null;
664         return propertyOperation.mergePropertiesAssociatedToNode(NodeTypeEnum.GroupType, groupId, mapProperties)
665                 .right()
666                 .map(DaoStatusConverter::convertJanusGraphStatusToStorageStatus);
667     }
668
669
670
671     private Either<GraphRelation, StorageOperationStatus> updateGroupDerivedFrom(GroupTypeDefinition updatedGroupType, String currDerivedFromGroupType) {
672         
673         String groupTypeId = updatedGroupType.getUniqueId();
674         if (StringUtils.equals(updatedGroupType.getDerivedFrom(), currDerivedFromGroupType)) {
675             return Strings.isNullOrEmpty(currDerivedFromGroupType)? 
676                     Either.right(StorageOperationStatus.OK):
677                         getLatestGroupTypeByType(currDerivedFromGroupType, true)
678                         .left()
679                         .map(def -> null);
680         }
681         
682         StorageOperationStatus status = isLegalToReplaceParent(currDerivedFromGroupType, updatedGroupType.getDerivedFrom(), updatedGroupType.getType());
683         if ( status != StorageOperationStatus.OK) {
684             return Either.right(status);
685         }
686
687         log.debug("#updateGroupDerivedFrom - updating group derived from relation for group type with id {}. old derived type {}. new derived type {}", groupTypeId, currDerivedFromGroupType, updatedGroupType.getDerivedFrom());
688         StorageOperationStatus deleteDerivedRelationStatus = deleteDerivedFromGroupType(groupTypeId, currDerivedFromGroupType);
689         if (deleteDerivedRelationStatus != StorageOperationStatus.OK) {
690             return Either.right(deleteDerivedRelationStatus);
691         }
692         return addDerivedFromRelation(updatedGroupType, groupTypeId);
693     }
694
695     private StorageOperationStatus isLegalToReplaceParent(String oldTypeParent, String newTypeParent, String childType) {
696         return derivedFromOperation.isUpdateParentAllowed(oldTypeParent, newTypeParent, childType, NodeTypeEnum.GroupType, GroupTypeData.class, t -> t.getGroupTypeDataDefinition().getType());
697     }
698     
699     private Either<GraphRelation, StorageOperationStatus> addDerivedFromRelation(GroupTypeDataDefinition groupTypeDef, String gtUniqueId) {
700         String derivedFrom = groupTypeDef.getDerivedFrom();
701         if (derivedFrom == null) {
702             return Either.left(null);
703         }
704         log.debug("#addDerivedFromRelationBefore - adding derived from relation between group type {} to its parent {}", groupTypeDef.getType(), derivedFrom);
705         return this.getLatestGroupTypeByType(derivedFrom, true)
706                 .left()
707                 .bind(derivedFromGroup -> derivedFromOperation.addDerivedFromRelation(gtUniqueId, derivedFromGroup.getUniqueId(), NodeTypeEnum.GroupType));
708     }
709
710     private StorageOperationStatus deleteDerivedFromGroupType(String groupTypeId, String derivedFromType) {
711         if (derivedFromType == null) {
712             return StorageOperationStatus.OK;
713         }
714         log.debug("#deleteDerivedFromGroupType - deleting derivedFrom relation for group type with id {} and its derived type {}", groupTypeId, derivedFromType);
715         return getLatestGroupTypeByType(derivedFromType, true)
716                 .either(derivedFromNode -> derivedFromOperation.removeDerivedFromRelation(groupTypeId, derivedFromNode.getUniqueId(), NodeTypeEnum.GroupType),
717                         err -> err);
718     }
719
720     private void updateGroupTypeData(GroupTypeDefinition updatedTypeDefinition, GroupTypeDefinition currTypeDefinition) {
721         updatedTypeDefinition.setUniqueId(currTypeDefinition.getUniqueId());
722         updatedTypeDefinition.setCreationTime(currTypeDefinition.getCreationTime());
723         updatedTypeDefinition.setModificationTime(System.currentTimeMillis());
724     }
725
726 }