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