[SDC] sync
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / operations / impl / CapabilityTypeOperation.java
1 /*-
2  * ============LICENSE_START=======================================================
3  * SDC
4  * ================================================================================
5  * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
6  * ================================================================================
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  * 
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  * 
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  * ============LICENSE_END=========================================================
19  */
20
21 package org.openecomp.sdc.be.model.operations.impl;
22
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.HashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.stream.Collectors;
30
31 import org.apache.commons.lang3.tuple.ImmutablePair;
32 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
33 import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
34 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
35 import org.openecomp.sdc.be.dao.titan.TitanGenericDao;
36 import org.openecomp.sdc.be.dao.titan.TitanOperationStatus;
37 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
38 import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
39 import org.openecomp.sdc.be.model.PropertyDefinition;
40 import org.openecomp.sdc.be.model.operations.api.ICapabilityOperation;
41 import org.openecomp.sdc.be.model.operations.api.ICapabilityTypeOperation;
42 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
43 import org.openecomp.sdc.be.resources.data.CapabilityTypeData;
44 import org.openecomp.sdc.be.resources.data.PropertyData;
45 import org.openecomp.sdc.be.resources.data.UniqueIdData;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
48 import org.springframework.beans.factory.annotation.Autowired;
49 import org.springframework.stereotype.Component;
50
51 import fj.data.Either;
52
53 @Component("capability-type-operation")
54 public class CapabilityTypeOperation extends AbstractOperation implements ICapabilityTypeOperation {
55         @Autowired
56         private PropertyOperation propertyOperation;
57         @Autowired
58         private ICapabilityOperation capabilityOperation;
59
60         public CapabilityTypeOperation() {
61                 super();
62         }
63
64         private static Logger log = LoggerFactory.getLogger(CapabilityTypeOperation.class.getName());
65
66         /**
67          * FOR TEST ONLY
68          * 
69          * @param titanGenericDao
70          */
71         public void setTitanGenericDao(TitanGenericDao titanGenericDao) {
72                 this.titanGenericDao = titanGenericDao;
73         }
74
75         @Override
76         public Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType(CapabilityTypeDefinition capabilityTypeDefinition, boolean inTransaction) {
77
78                 Either<CapabilityTypeDefinition, StorageOperationStatus> result = null;
79
80                 try {
81                         Either<CapabilityTypeDefinition, TitanOperationStatus> validationRes = validateUpdateProperties(capabilityTypeDefinition);
82                         if (validationRes.isRight()) {
83                                 log.error("One or all properties of capability type {} not valid. status is {}", capabilityTypeDefinition, validationRes.right().value().name());
84                                 result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(validationRes.right().value()));
85                                 return result;
86                         }
87                         Either<CapabilityTypeData, TitanOperationStatus> eitherStatus = addCapabilityTypeToGraph(capabilityTypeDefinition);
88
89                         if (eitherStatus.isRight()) {
90                                 log.error("Failed to add capability {} to Graph. status is {}", capabilityTypeDefinition, eitherStatus.right().value().name());
91                                 result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(eitherStatus.right().value()));
92                                 return result;
93                         } else {
94                                 CapabilityTypeData capabilityTypeData = eitherStatus.left().value();
95
96                                 CapabilityTypeDefinition capabilityTypeDefResult = convertCTDataToCTDefinition(capabilityTypeData);
97                                 log.debug("The returned CapabilityTypeDefinition is {}", capabilityTypeDefResult);
98                                 result = Either.left(capabilityTypeDefResult);
99                                 return result;
100                         }
101                 }
102
103                 finally {
104                         if (false == inTransaction) {
105                                 if (result == null || result.isRight()) {
106                                         log.error("Going to execute rollback on graph.");
107                                         titanGenericDao.rollback();
108                                 } else {
109                                         log.debug("Going to execute commit on graph.");
110                                         titanGenericDao.commit();
111                                 }
112                         }
113                 }
114
115         }
116
117         private Either<CapabilityTypeDefinition, TitanOperationStatus> validateUpdateProperties(CapabilityTypeDefinition capabilityTypeDefinition) {
118                 TitanOperationStatus error = null;
119                 if (capabilityTypeDefinition.getProperties() != null && !capabilityTypeDefinition.getProperties().isEmpty() && capabilityTypeDefinition.getDerivedFrom() != null) {
120                         Either<Map<String, PropertyDefinition>, TitanOperationStatus> allPropertiesRes = capabilityOperation.getAllCapabilityTypePropertiesFromAllDerivedFrom(capabilityTypeDefinition.getDerivedFrom());
121                         if (allPropertiesRes.isRight() && !allPropertiesRes.right().value().equals(TitanOperationStatus.NOT_FOUND)) {
122                                 error = allPropertiesRes.right().value();
123                                 log.debug("Couldn't fetch derived from property nodes for capability type {}, error: {}", capabilityTypeDefinition.getType(), error);
124                         }
125                         if (error == null && !allPropertiesRes.left().value().isEmpty()) {
126                                 Map<String, PropertyDefinition> derivedFromProperties = allPropertiesRes.left().value();
127                                 capabilityTypeDefinition.getProperties().entrySet().stream().filter(e -> derivedFromProperties.containsKey(e.getKey()) && e.getValue().getType() == null)
128                                                 .forEach(e -> e.getValue().setType(derivedFromProperties.get(e.getKey()).getType()));
129
130                                 Either<List<PropertyDefinition>, TitanOperationStatus> validatePropertiesRes = capabilityOperation.validatePropertyUniqueness(allPropertiesRes.left().value(),
131                                                 capabilityTypeDefinition.getProperties().values().stream().collect(Collectors.toList()));
132                                 if (validatePropertiesRes.isRight()) {
133                                         error = validatePropertiesRes.right().value();
134                                 }
135                         }
136                 }
137                 if (error == null) {
138                         return Either.left(capabilityTypeDefinition);
139                 }
140                 return Either.right(error);
141         }
142
143         /**
144          * 
145          * convert between graph Node object to Java object
146          * 
147          * @param capabilityTypeData
148          * @return
149          */
150         protected CapabilityTypeDefinition convertCTDataToCTDefinition(CapabilityTypeData capabilityTypeData) {
151                 log.debug("The object returned after create capability is {}", capabilityTypeData);
152
153                 CapabilityTypeDefinition capabilityTypeDefResult = new CapabilityTypeDefinition(capabilityTypeData.getCapabilityTypeDataDefinition());
154
155                 return capabilityTypeDefResult;
156         }
157
158         /**
159          * 
160          * Add capability type to graph.
161          * 
162          * 1. Add capability type node
163          * 
164          * 2. Add edge between the former node to its parent(if exists)
165          * 
166          * 3. Add property node and associate it to the node created at #1. (per property & if exists)
167          * 
168          * @param capabilityTypeDefinition
169          * @return
170          */
171         private Either<CapabilityTypeData, TitanOperationStatus> addCapabilityTypeToGraph(CapabilityTypeDefinition capabilityTypeDefinition) {
172
173                 log.debug("Got capability type {}", capabilityTypeDefinition);
174
175                 String ctUniqueId = UniqueIdBuilder.buildCapabilityTypeUid(capabilityTypeDefinition.getType());
176                 // capabilityTypeDefinition.setUniqueId(ctUniqueId);
177
178                 CapabilityTypeData capabilityTypeData = buildCapabilityTypeData(capabilityTypeDefinition, ctUniqueId);
179
180                 log.debug("Before adding capability type to graph. capabilityTypeData = {}", capabilityTypeData);
181                 Either<CapabilityTypeData, TitanOperationStatus> createCTResult = titanGenericDao.createNode(capabilityTypeData, CapabilityTypeData.class);
182                 log.debug("After adding capability type to graph. status is = {}", createCTResult);
183
184                 if (createCTResult.isRight()) {
185                         TitanOperationStatus operationStatus = createCTResult.right().value();
186                         log.error("Failed to capability type {} to graph. status is {}", capabilityTypeDefinition.getType(), operationStatus);
187                         return Either.right(operationStatus);
188                 }
189
190                 CapabilityTypeData resultCTD = createCTResult.left().value();
191                 Map<String, PropertyDefinition> propertiesMap = capabilityTypeDefinition.getProperties();
192                 Collection<PropertyDefinition> properties = propertiesMap != null ? propertiesMap.values() : null;
193                 Either<Map<String, PropertyData>, TitanOperationStatus> addPropertiesToCapablityType = propertyOperation.addPropertiesToElementType(resultCTD.getUniqueId(), NodeTypeEnum.CapabilityType, propertiesMap);
194                 if (addPropertiesToCapablityType.isRight()) {
195                         log.error("Failed add properties {} to capability {}", propertiesMap, capabilityTypeDefinition.getType());
196                         return Either.right(addPropertiesToCapablityType.right().value());
197                 }
198
199                 String derivedFrom = capabilityTypeDefinition.getDerivedFrom();
200                 if (derivedFrom != null) {
201                         log.debug("Before creating relation between capability type {} to its parent {}", ctUniqueId, derivedFrom);
202                         UniqueIdData from = new UniqueIdData(NodeTypeEnum.CapabilityType, ctUniqueId);
203                         UniqueIdData to = new UniqueIdData(NodeTypeEnum.CapabilityType, derivedFrom);
204                         Either<GraphRelation, TitanOperationStatus> createRelation = titanGenericDao.createRelation(from, to, GraphEdgeLabels.DERIVED_FROM, null);
205                         log.debug("After create relation between capability type {} to its parent {}. status is {}", ctUniqueId, derivedFrom, createRelation);
206                         if (createRelation.isRight()) {
207                                 return Either.right(createRelation.right().value());
208                         }
209                 }
210
211                 return Either.left(createCTResult.left().value());
212
213         }
214
215         private CapabilityTypeData buildCapabilityTypeData(CapabilityTypeDefinition capabilityTypeDefinition, String ctUniqueId) {
216
217                 CapabilityTypeData capabilityTypeData = new CapabilityTypeData(capabilityTypeDefinition);
218
219                 capabilityTypeData.getCapabilityTypeDataDefinition().setUniqueId(ctUniqueId);
220                 Long creationDate = capabilityTypeData.getCapabilityTypeDataDefinition().getCreationTime();
221                 if (creationDate == null) {
222                         creationDate = System.currentTimeMillis();
223                 }
224                 capabilityTypeData.getCapabilityTypeDataDefinition().setCreationTime(creationDate);
225                 capabilityTypeData.getCapabilityTypeDataDefinition().setModificationTime(creationDate);
226                 return capabilityTypeData;
227         }
228
229         @Override
230         public Either<CapabilityTypeDefinition, StorageOperationStatus> getCapabilityType(String uniqueId, boolean inTransaction) {
231
232                 Either<CapabilityTypeDefinition, StorageOperationStatus> result = null;
233                 try {
234
235                         Either<CapabilityTypeDefinition, TitanOperationStatus> ctResult = this.getCapabilityTypeByUid(uniqueId);
236
237                         if (ctResult.isRight()) {
238                                 TitanOperationStatus status = ctResult.right().value();
239                                 if (status != TitanOperationStatus.NOT_FOUND) {
240                                         log.error("Failed to retrieve information on capability type {}. status is {}", uniqueId, status);
241                                 }
242                                 result = Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(ctResult.right().value()));
243                                 return result;
244                         }
245
246                         result = Either.left(ctResult.left().value());
247
248                         return result;
249                 } finally {
250                         if (false == inTransaction) {
251                                 log.debug("Going to execute commit on graph.");
252                                 titanGenericDao.commit();
253                         }
254                 }
255         }
256
257         /**
258          * Build Capability type object from graph by unique id
259          * 
260          * @param uniqueId
261          * @return
262          */
263         public Either<CapabilityTypeDefinition, TitanOperationStatus> getCapabilityTypeByUid(String uniqueId) {
264
265                 Either<CapabilityTypeDefinition, TitanOperationStatus> result = null;
266
267                 Either<CapabilityTypeData, TitanOperationStatus> capabilityTypesRes = titanGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), uniqueId, CapabilityTypeData.class);
268
269                 if (capabilityTypesRes.isRight()) {
270                         TitanOperationStatus status = capabilityTypesRes.right().value();
271                         log.debug("Capability type {} cannot be found in graph. status is {}", uniqueId, status);
272                         return Either.right(status);
273                 }
274
275                 CapabilityTypeData ctData = capabilityTypesRes.left().value();
276                 CapabilityTypeDefinition capabilityTypeDefinition = new CapabilityTypeDefinition(ctData.getCapabilityTypeDataDefinition());
277
278                 TitanOperationStatus propertiesStatus = fillProperties(uniqueId, capabilityTypeDefinition);
279                 if (propertiesStatus != TitanOperationStatus.OK) {
280                         log.error("Failed to fetch properties of capability type {}", uniqueId);
281                         return Either.right(propertiesStatus);
282                 }
283
284                 Either<ImmutablePair<CapabilityTypeData, GraphEdge>, TitanOperationStatus> parentNode = titanGenericDao.getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), uniqueId, GraphEdgeLabels.DERIVED_FROM,
285                                 NodeTypeEnum.CapabilityType, CapabilityTypeData.class);
286                 log.debug("After retrieving DERIVED_FROM node of {}. status is {}", uniqueId, parentNode);
287                 if (parentNode.isRight()) {
288                         TitanOperationStatus titanOperationStatus = parentNode.right().value();
289                         if (titanOperationStatus != TitanOperationStatus.NOT_FOUND) {
290                                 log.error("Failed to find the parent capability of capability type {}. status is {}", uniqueId, titanOperationStatus);
291                                 result = Either.right(titanOperationStatus);
292                                 return result;
293                         }
294                 } else {
295                         // derived from node was found
296                         ImmutablePair<CapabilityTypeData, GraphEdge> immutablePair = parentNode.left().value();
297                         CapabilityTypeData parentCT = immutablePair.getKey();
298                         capabilityTypeDefinition.setDerivedFrom(parentCT.getCapabilityTypeDataDefinition().getType());
299                 }
300                 result = Either.left(capabilityTypeDefinition);
301
302                 return result;
303         }
304
305         private TitanOperationStatus fillProperties(String uniqueId, CapabilityTypeDefinition capabilityTypeDefinition) {
306
307                 Either<Map<String, PropertyDefinition>, TitanOperationStatus> findPropertiesOfNode = propertyOperation.findPropertiesOfNode(NodeTypeEnum.CapabilityType, uniqueId);
308                 if (findPropertiesOfNode.isRight()) {
309                         TitanOperationStatus titanOperationStatus = findPropertiesOfNode.right().value();
310                         log.debug("After looking for properties of vertex {}. status is {}", uniqueId, titanOperationStatus);
311                         if (TitanOperationStatus.NOT_FOUND.equals(titanOperationStatus)) {
312                                 return TitanOperationStatus.OK;
313                         } else {
314                                 return titanOperationStatus;
315                         }
316                 } else {
317                         Map<String, PropertyDefinition> properties = findPropertiesOfNode.left().value();
318                         capabilityTypeDefinition.setProperties(properties);
319                         return TitanOperationStatus.OK;
320                 }
321         }
322
323         public Either<Boolean, StorageOperationStatus> isCapabilityTypeDerivedFrom(String childCandidateType, String parentCandidateType) {
324                 Map<String, Object> propertiesToMatch = new HashMap<String, Object>();
325                 propertiesToMatch.put(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), childCandidateType);
326                 Either<List<CapabilityTypeData>, TitanOperationStatus> getResponse = titanGenericDao.getByCriteria(NodeTypeEnum.CapabilityType, propertiesToMatch, CapabilityTypeData.class);
327                 if (getResponse.isRight()) {
328                         TitanOperationStatus titanOperationStatus = getResponse.right().value();
329                         log.debug("Couldn't fetch capability type {}, error: {}", childCandidateType, titanOperationStatus);
330                         return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(titanOperationStatus));
331                 }
332                 String childUniqueId = getResponse.left().value().get(0).getUniqueId();
333                 Set<String> travelledTypes = new HashSet<>();
334                 do {
335                         travelledTypes.add(childUniqueId);
336                         Either<List<ImmutablePair<CapabilityTypeData, GraphEdge>>, TitanOperationStatus> childrenNodes = titanGenericDao.getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.CapabilityType), childUniqueId, GraphEdgeLabels.DERIVED_FROM,
337                                         NodeTypeEnum.CapabilityType, CapabilityTypeData.class);
338                         if (childrenNodes.isRight()) {
339                                 if (childrenNodes.right().value() != TitanOperationStatus.NOT_FOUND) {
340                                         TitanOperationStatus titanOperationStatus = getResponse.right().value();
341                                         log.debug("Couldn't fetch derived from node for capability type {}, error: {}", childCandidateType, titanOperationStatus);
342                                         return Either.right(DaoStatusConverter.convertTitanStatusToStorageStatus(titanOperationStatus));
343                                 } else {
344                                         log.debug("Derived from node is not found for type {} - this is OK for root capability.");
345                                         return Either.left(false);
346                                 }
347                         }
348                         String derivedFromUniqueId = childrenNodes.left().value().get(0).getLeft().getUniqueId();
349                         if (derivedFromUniqueId.equals(parentCandidateType)) {
350                                 log.debug("Verified that capability type {} derives from capability type {}", childCandidateType, parentCandidateType);
351                                 return Either.left(true);
352                         }
353                         childUniqueId = derivedFromUniqueId;
354                 } while (!travelledTypes.contains(childUniqueId));
355                 // this stop condition should never be used, if we use it, we have an
356                 // illegal cycle in graph - "derived from" hierarchy cannot be cycled.
357                 // It's here just to avoid infinite loop in case we have such cycle.
358                 log.error("Detected a cycle of \"derived from\" edges starting at capability type node {}", childUniqueId);
359                 return Either.right(StorageOperationStatus.GENERAL_ERROR);
360         }
361
362         /**
363          * FOR TEST ONLY
364          * 
365          * @param propertyOperation
366          */
367         public void setPropertyOperation(PropertyOperation propertyOperation) {
368                 this.propertyOperation = propertyOperation;
369         }
370
371         @Override
372         public Either<CapabilityTypeDefinition, StorageOperationStatus> addCapabilityType(CapabilityTypeDefinition capabilityTypeDefinition) {
373
374                 return addCapabilityType(capabilityTypeDefinition, false);
375         }
376
377         @Override
378         public Either<CapabilityTypeDefinition, StorageOperationStatus> getCapabilityType(String uniqueId) {
379                 return getCapabilityType(uniqueId, false);
380         }
381
382 }