2 * ============LICENSE_START=======================================================
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
11 * http://www.apache.org/licenses/LICENSE-2.0
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=========================================================
21 package org.openecomp.sdc.be.model.operations.impl;
23 import static org.openecomp.sdc.be.dao.titan.TitanUtils.buildNotInPredicate;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.List;
29 import java.util.Optional;
31 import java.util.stream.Collectors;
33 import javax.annotation.Resource;
35 import org.apache.commons.collections.CollectionUtils;
36 import org.apache.commons.lang3.tuple.ImmutablePair;
37 import org.apache.tinkerpop.gremlin.structure.Edge;
38 import org.openecomp.sdc.be.config.BeEcompErrorManager;
39 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
40 import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
41 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
42 import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
43 import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
44 import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
45 import org.openecomp.sdc.be.datatypes.elements.GroupTypeDataDefinition;
46 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
47 import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
48 import org.openecomp.sdc.be.model.GroupTypeDefinition;
49 import org.openecomp.sdc.be.model.PropertyDefinition;
50 import org.openecomp.sdc.be.model.operations.StorageException;
51 import org.openecomp.sdc.be.model.operations.api.IGroupTypeOperation;
52 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
53 import org.openecomp.sdc.be.resources.data.CapabilityTypeData;
54 import org.openecomp.sdc.be.resources.data.GroupTypeData;
55 import org.openecomp.sdc.be.resources.data.PropertyData;
56 import org.openecomp.sdc.be.resources.data.UniqueIdData;
57 import org.slf4j.Logger;
58 import org.slf4j.LoggerFactory;
59 import org.springframework.beans.factory.annotation.Qualifier;
60 import org.springframework.stereotype.Component;
62 import com.google.common.base.Strings;
63 import com.thinkaurelius.titan.graphdb.query.TitanPredicate;
65 import fj.data.Either;
67 @Component("group-type-operation")
68 public class GroupTypeOperation extends AbstractOperation implements IGroupTypeOperation {
70 private CapabilityTypeOperation capabilityTypeOperation;
72 private static final Logger log = LoggerFactory.getLogger(GroupTypeOperation.class);
74 private static final String CREATE_FLOW_CONTEXT = "CreateGroupType";
75 private static final String GET_FLOW_CONTEXT = "GetGroupType";
77 private PropertyOperation propertyOperation;
79 private TitanGenericDao titanGenericDao;
81 public GroupTypeOperation(@Qualifier("titan-generic-dao") TitanGenericDao titanGenericDao, @Qualifier("property-operation") PropertyOperation propertyOperation) {
83 this.propertyOperation = propertyOperation;
84 this.titanGenericDao = titanGenericDao;
90 * @param titanGenericDao
92 public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
93 this.titanGenericDao = titanGenericDao;
97 public Either<GroupTypeDefinition, StorageOperationStatus> addGroupType(GroupTypeDefinition groupTypeDefinition) {
99 return addGroupType(groupTypeDefinition, false);
103 public Either<GroupTypeDefinition, StorageOperationStatus> addGroupType(GroupTypeDefinition groupTypeDefinition, boolean inTransaction) {
105 Either<GroupTypeDefinition, StorageOperationStatus> result = null;
109 Either<GroupTypeData, TitanOperationStatus> eitherStatus = addGroupTypeToGraph(groupTypeDefinition);
111 if (eitherStatus.isRight()) {
112 BeEcompErrorManager.getInstance().logBeFailedCreateNodeError(CREATE_FLOW_CONTEXT, groupTypeDefinition.getType(), eitherStatus.right().value().name());
113 result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(eitherStatus.right().value()));
116 GroupTypeData groupTypeData = eitherStatus.left().value();
118 String uniqueId = groupTypeData.getUniqueId();
119 Either<GroupTypeDefinition, StorageOperationStatus> groupTypeRes = this.getGroupType(uniqueId, true);
121 if (groupTypeRes.isRight()) {
122 BeEcompErrorManager.getInstance().logBeFailedRetrieveNodeError(GET_FLOW_CONTEXT, groupTypeDefinition.getType(), eitherStatus.right().value().name());
124 List<CapabilityTypeDefinition> groupCapTypes = groupTypeDefinition.getCapabilityTypes();
125 if (!CollectionUtils.isEmpty(groupCapTypes)) {
126 Optional<TitanOperationStatus> firstFailure = connectToCapabilityType(groupTypeData, groupCapTypes);
127 if (firstFailure.isPresent()) {
128 groupTypeRes = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(firstFailure.get()));
133 result = groupTypeRes;
140 handleTransactionCommitRollback(inTransaction, result);
147 public Either<GroupTypeDefinition, StorageOperationStatus> upgradeGroupType(GroupTypeDefinition groupTypeDefinitionNew, GroupTypeDefinition groupTypeDefinitionOld) {
148 return upgradeGroupType(groupTypeDefinitionOld, groupTypeDefinitionNew, false);
152 public Either<GroupTypeDefinition, StorageOperationStatus> upgradeGroupType(GroupTypeDefinition groupTypeDefinitionNew, GroupTypeDefinition groupTypeDefinitionOld, boolean inTransaction) {
153 Either<GroupTypeDefinition, StorageOperationStatus> result = Either.left(groupTypeDefinitionNew);
157 // Right now upgrade Group is used only to ensure that already existing group type is connected by DERRIVED_FROM edge with it's parent
158 // We don't need to use for a while new node definition since following group type upgrade is not supported.
159 if (!Strings.isNullOrEmpty(groupTypeDefinitionOld.getDerivedFrom())) {
160 result = ensureExsitanceDerivedFromEdge(groupTypeDefinitionOld);
163 handleTransactionCommitRollback(inTransaction, result);
169 private Optional<TitanOperationStatus> connectToCapabilityType(GroupTypeData groupTypeData, List<CapabilityTypeDefinition> groupCapTypes) {
170 return groupCapTypes.stream()
171 .map(groupCapTypeDef -> connectTo(groupTypeData, groupCapTypeDef))
172 .filter(Either::isRight)
174 .map(either -> either.right().value());
177 private Either<GraphRelation, TitanOperationStatus> connectTo(GroupTypeData groupTypeData, CapabilityTypeDefinition groupCapTypeDef) {
178 Either<CapabilityTypeData, TitanOperationStatus> eitherCapData = capabilityTypeOperation.getCapabilityTypeByType(groupCapTypeDef.getType());
179 if (eitherCapData.isLeft()) {
180 return titanGenericDao.createRelation(groupTypeData, eitherCapData.left().value(), GraphEdgeLabels.GROUP_TYPE_CAPABILITY_TYPE, null);
183 return Either.right(eitherCapData.right().value());
186 public List<GroupTypeDefinition> getAllGroupTypes(Set<String> excludedGroupTypes) {
187 Map<String, Map.Entry<TitanPredicate, Object>> predicateCriteria = buildNotInPredicate(GraphPropertiesDictionary.TYPE.getProperty(), excludedGroupTypes);
188 List<GroupTypeData> groupTypes = titanGenericDao.getByCriteriaWithPredicate(NodeTypeEnum.GroupType, predicateCriteria, GroupTypeData.class)
190 .on(this::onTitanAccessError);
192 return convertGroupTypesToDefinition(groupTypes);
196 private List<GroupTypeDefinition> convertGroupTypesToDefinition(List<GroupTypeData> groupTypes) {
197 return groupTypes.stream()
198 .map(type -> new GroupTypeDefinition(type.getGroupTypeDataDefinition()))
199 .collect(Collectors.toList());
202 private List<GroupTypeData> onTitanAccessError(TitanOperationStatus toe) {
203 throw new StorageException(
204 DaoStatusConverter.convertTitanStatusToStorageStatus(toe));
208 public Either<GroupTypeDefinition, TitanOperationStatus> getGroupTypeByUid(String uniqueId) {
210 Either<GroupTypeDefinition, TitanOperationStatus> result = null;
212 Either<GroupTypeData, TitanOperationStatus> groupTypesRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.GroupType), uniqueId, GroupTypeData.class);
214 if (groupTypesRes.isRight()) {
215 TitanOperationStatus status = groupTypesRes.right().value();
216 log.debug("Group type {} cannot be found in graph. status is {}", uniqueId, status);
217 return Either.right(status);
220 GroupTypeData gtData = groupTypesRes.left().value();
221 GroupTypeDefinition groupTypeDefinition = new GroupTypeDefinition(gtData.getGroupTypeDataDefinition());
223 TitanOperationStatus propertiesStatus = propertyOperation.fillProperties(uniqueId, NodeTypeEnum.GroupType, properList -> groupTypeDefinition.setProperties(properList));
225 if (propertiesStatus != TitanOperationStatus.OK) {
226 log.error("Failed to fetch properties of capability type {}", uniqueId);
227 return Either.right(propertiesStatus);
230 result = Either.left(groupTypeDefinition);
236 public Either<GroupTypeDefinition, StorageOperationStatus> getGroupType(String uniqueId) {
238 return getGroupType(uniqueId, false);
243 public Either<GroupTypeDefinition, StorageOperationStatus> getGroupType(String uniqueId, boolean inTransaction) {
244 return getElementType(this::getGroupTypeByUid, uniqueId, inTransaction);
248 public Either<GroupTypeDefinition, StorageOperationStatus> getLatestGroupTypeByType(String type) {
249 return getLatestGroupTypeByType(type, false);
253 public Either<GroupTypeDefinition, StorageOperationStatus> getLatestGroupTypeByType(String type, boolean inTransaction) {
254 Map<String, Object> mapCriteria = new HashMap<>();
255 mapCriteria.put(GraphPropertiesDictionary.TYPE.getProperty(), type);
256 mapCriteria.put(GraphPropertiesDictionary.IS_HIGHEST_VERSION.getProperty(), true);
258 return getGroupTypeByCriteria(type, mapCriteria, inTransaction);
262 public Either<GroupTypeDefinition, StorageOperationStatus> getGroupTypeByCriteria(String type, Map<String, Object> properties, boolean inTransaction) {
263 Either<GroupTypeDefinition, StorageOperationStatus> result = null;
265 if (type == null || type.isEmpty()) {
266 log.error("type is empty");
267 result = Either.right(StorageOperationStatus.INVALID_ID);
271 Either<List<GroupTypeData>, TitanOperationStatus> groupTypeEither = titanGenericDao.getByCriteria(NodeTypeEnum.GroupType, properties, GroupTypeData.class);
272 if (groupTypeEither.isRight()) {
273 result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(groupTypeEither.right().value()));
275 GroupTypeDataDefinition dataDefinition = groupTypeEither.left().value().stream().map(e -> e.getGroupTypeDataDefinition()).findFirst().get();
276 result = getGroupType(dataDefinition.getUniqueId(), inTransaction);
282 handleTransactionCommitRollback(inTransaction, result);
287 public Either<GroupTypeDefinition, StorageOperationStatus> getGroupTypeByTypeAndVersion(String type, String version) {
288 return getGroupTypeByTypeAndVersion(type, version, false);
292 public Either<GroupTypeDefinition, StorageOperationStatus> getGroupTypeByTypeAndVersion(String type, String version, boolean inTransaction) {
293 Map<String, Object> mapCriteria = new HashMap<>();
294 mapCriteria.put(GraphPropertiesDictionary.TYPE.getProperty(), type);
295 mapCriteria.put(GraphPropertiesDictionary.VERSION.getProperty(), version);
297 return getGroupTypeByCriteria(type, mapCriteria, inTransaction);
301 * Add group type to graph.
303 * 1. Add group type node
305 * 2. Add edge between the former node to its parent(if exists)
307 * 3. Add property node and associate it to the node created at #1. (per property & if exists)
309 * @param groupTypeDefinition
312 private Either<GroupTypeData, TitanOperationStatus> addGroupTypeToGraph(GroupTypeDefinition groupTypeDefinition) {
314 log.debug("Got group type {}", groupTypeDefinition);
316 String ctUniqueId = UniqueIdBuilder.buildGroupTypeUid(groupTypeDefinition.getType(), groupTypeDefinition.getVersion());
318 GroupTypeData groupTypeData = buildGroupTypeData(groupTypeDefinition, ctUniqueId);
320 log.debug("Before adding group type to graph. groupTypeData = {}", groupTypeData);
322 Either<GroupTypeData, TitanOperationStatus> createGTResult = titanGenericDao.createNode(groupTypeData, GroupTypeData.class);
323 log.debug("After adding group type to graph. status is = {}", createGTResult);
325 if (createGTResult.isRight()) {
326 TitanOperationStatus operationStatus = createGTResult.right().value();
327 log.error("Failed to add group type {} to graph. status is {}", groupTypeDefinition.getType(), operationStatus);
328 return Either.right(operationStatus);
331 GroupTypeData resultCTD = createGTResult.left().value();
332 List<PropertyDefinition> properties = groupTypeDefinition.getProperties();
333 Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesToCapablityType = propertyOperation.addPropertiesToElementType(resultCTD.getUniqueId(), NodeTypeEnum.GroupType, properties);
334 if (addPropertiesToCapablityType.isRight()) {
335 log.error("Failed add properties {} to capability {}", properties, groupTypeDefinition.getType());
336 return Either.right(addPropertiesToCapablityType.right().value());
339 String derivedFrom = groupTypeDefinition.getDerivedFrom();
340 if (derivedFrom != null) {
341 Either<GraphRelation, TitanOperationStatus> createRelation = connectToDerivedFrom(ctUniqueId, derivedFrom);
342 if (createRelation.isRight()) {
343 return Either.right(createRelation.right().value());
347 return Either.left(createGTResult.left().value());
352 private Either<GraphRelation, TitanOperationStatus> connectToDerivedFrom(String ctUniqueId, String derivedFrom) {
353 log.debug("Before creating relation between Group Type with id {} to its parent {}", ctUniqueId, derivedFrom);
355 Either<GroupTypeData, TitanOperationStatus> derivedFromGroupTypeResult =
356 titanGenericDao.getNode(GraphPropertiesDictionary.TYPE.getProperty(), derivedFrom, GroupTypeData.class);
358 if (derivedFromGroupTypeResult.isLeft()) {
359 UniqueIdData from = new UniqueIdData(NodeTypeEnum.GroupType, ctUniqueId);
360 GroupTypeData to = derivedFromGroupTypeResult.left().value();
362 Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(from, to, GraphEdgeLabels.DERIVED_FROM, null);
363 log.debug("After create relation between Group Type with id {} to its parent {}, status is {}.", ctUniqueId, derivedFrom, createRelation);
364 return createRelation;
366 TitanOperationStatus status = derivedFromGroupTypeResult.right().value();
367 log.debug("Failed to found parent Group Type {}, stauts is {}.", derivedFrom, status);
368 return Either.right(status);
373 private Either<GroupTypeDefinition, StorageOperationStatus> ensureExsitanceDerivedFromEdge(GroupTypeDefinition groupTypeDefinition) {
374 Either<GroupTypeDefinition, StorageOperationStatus> result = Either.left(groupTypeDefinition);
376 GroupTypeData childGroupType = null;
377 GroupTypeData parentGroupType = null;
379 Either<GroupTypeData, TitanOperationStatus> childGroupTypeResult =
380 titanGenericDao.getNode(GraphPropertiesDictionary.TYPE.getProperty(), groupTypeDefinition.getType(), GroupTypeData.class);
381 if (childGroupTypeResult.isRight()) {
382 result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(childGroupTypeResult.right().value()));
383 log.debug("Filed to find GroupType with type {}, status is {}.", groupTypeDefinition.getType(), childGroupTypeResult);
385 childGroupType = childGroupTypeResult.left().value();
389 if (result.isLeft()) {
390 Either<GroupTypeData, TitanOperationStatus> parentGroupTypeResult =
391 titanGenericDao.getNode(GraphPropertiesDictionary.TYPE.getProperty(), groupTypeDefinition.getDerivedFrom(), GroupTypeData.class);
392 if (parentGroupTypeResult.isRight()) {
393 result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(parentGroupTypeResult.right().value()));
394 log.debug("Filed to find GroupType with type {}, status is {}.", groupTypeDefinition.getDerivedFrom(), parentGroupTypeResult);
396 parentGroupType = parentGroupTypeResult.left().value();
401 if (childGroupType != null && parentGroupType != null) {
402 Either<Edge, TitanOperationStatus> edgeDerivedFromResult = titanGenericDao.getEdgeByNodes(childGroupType, parentGroupType, GraphEdgeLabels.DERIVED_FROM);
403 if (edgeDerivedFromResult.isLeft()) {
404 log.debug("It was found relation {}. Don't need to create the edge.", edgeDerivedFromResult.left().value());
406 Either<GraphRelation, TitanOperationStatus> createRelationResult = titanGenericDao.createRelation(childGroupType, parentGroupType, GraphEdgeLabels.DERIVED_FROM, null);
407 log.debug("After create relation between Group Type with id {} to its parent with id {}, status is {}.",
408 childGroupType.getKeyValueId().getValue(), parentGroupType.getKeyValueId().getValue(), createRelationResult);
409 if (createRelationResult.isRight()) {
410 result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(createRelationResult.right().value()));
421 private GroupTypeData buildGroupTypeData(GroupTypeDefinition groupTypeDefinition, String ctUniqueId) {
423 GroupTypeData groupTypeData = new GroupTypeData(groupTypeDefinition);
425 groupTypeData.getGroupTypeDataDefinition().setUniqueId(ctUniqueId);
426 Long creationDate = groupTypeData.getGroupTypeDataDefinition().getCreationTime();
427 if (creationDate == null) {
428 creationDate = System.currentTimeMillis();
430 groupTypeData.getGroupTypeDataDefinition().setCreationTime(creationDate);
431 groupTypeData.getGroupTypeDataDefinition().setModificationTime(creationDate);
433 return groupTypeData;
436 public Either<Boolean, StorageOperationStatus> isCapabilityTypeDerivedFrom(String childCandidateType, String parentCandidateType) {
437 Map<String, Object> propertiesToMatch = new HashMap<String, Object>();
438 propertiesToMatch.put(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), childCandidateType);
439 Either<List<CapabilityTypeData>, TitanOperationStatus> getResponse = titanGenericDao.getByCriteria(NodeTypeEnum.CapabilityType, propertiesToMatch, CapabilityTypeData.class);
440 if (getResponse.isRight()) {
441 TitanOperationStatus titanOperationStatus = getResponse.right().value();
442 log.debug("Couldn't fetch capability type {}, error: {}", childCandidateType, titanOperationStatus);
443 return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(titanOperationStatus));
445 String childUniqueId = getResponse.left().value().get(0).getUniqueId();
446 Set<String> travelledTypes = new HashSet<>();
448 travelledTypes.add(childUniqueId);
449 Either<List<ImmutablePair<CapabilityTypeData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), childUniqueId, GraphEdgeLabels.DERIVED_FROM,
450 NodeTypeEnum.CapabilityType, CapabilityTypeData.class);
451 if (childrenNodes.isRight()) {
452 if (childrenNodes.right().value() != TitanOperationStatus.NOT_FOUND) {
453 TitanOperationStatus titanOperationStatus = getResponse.right().value();
454 log.debug("Couldn't fetch derived from node for capability type {}, error: {}", childCandidateType, titanOperationStatus);
455 return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(titanOperationStatus));
457 log.debug("Derived from node is not found for type {} - this is OK for root capability.");
458 return Either.left(false);
461 String derivedFromUniqueId = childrenNodes.left().value().get(0).getLeft().getUniqueId();
462 if (derivedFromUniqueId.equals(parentCandidateType)) {
463 log.debug("Verified that capability type {} derives from capability type {}", childCandidateType, parentCandidateType);
464 return Either.left(true);
466 childUniqueId = derivedFromUniqueId;
467 } while (!travelledTypes.contains(childUniqueId));
468 // this stop condition should never be used, if we use it, we have an
469 // illegal cycle in graph - "derived from" hierarchy cannot be cycled.
470 // It's here just to avoid infinite loop in case we have such cycle.
471 log.error("Detected a cycle of \"derived from\" edges starting at capability type node {}", childUniqueId);
472 return Either.right(StorageOperationStatus.GENERAL_ERROR);
478 * @param propertyOperation
480 public void setPropertyOperation(PropertyOperation propertyOperation) {
481 this.propertyOperation = propertyOperation;
485 public Either<GroupTypeData, TitanOperationStatus> getLatestGroupTypeByNameFromGraph(String name) {