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