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