Upgrade SDC from Titan to Janus Graph
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / operations / impl / CapabilityOperation.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 static org.springframework.util.CollectionUtils.isEmpty;
24
25 import com.google.common.annotations.VisibleForTesting;
26 import com.google.common.base.Strings;
27 import fj.data.Either;
28 import java.util.ArrayList;
29 import java.util.HashMap;
30 import java.util.List;
31 import java.util.Map;
32 import java.util.Optional;
33 import java.util.Set;
34 import java.util.function.Function;
35 import java.util.stream.Collectors;
36 import org.apache.commons.lang3.tuple.ImmutablePair;
37 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
38 import org.openecomp.sdc.be.dao.graph.datatype.GraphRelation;
39 import org.openecomp.sdc.be.dao.janusgraph.HealingJanusGraphGenericDao;
40 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
41 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
42 import org.openecomp.sdc.be.dao.neo4j.GraphEdgePropertiesDictionary;
43 import org.openecomp.sdc.be.dao.neo4j.GraphPropertiesDictionary;
44 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
45 import org.openecomp.sdc.be.model.CapabilityDefinition;
46 import org.openecomp.sdc.be.model.CapabilityTypeDefinition;
47 import org.openecomp.sdc.be.model.ComponentInstanceProperty;
48 import org.openecomp.sdc.be.model.PropertyDefinition;
49 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
50 import org.openecomp.sdc.be.resources.data.CapabilityData;
51 import org.openecomp.sdc.be.resources.data.CapabilityTypeData;
52 import org.openecomp.sdc.be.resources.data.PropertyData;
53 import org.openecomp.sdc.be.resources.data.PropertyValueData;
54 import org.openecomp.sdc.common.log.wrappers.Logger;
55 import org.springframework.stereotype.Component;
56
57 @Component("capability-operation")
58 public class CapabilityOperation extends AbstractOperation {
59
60
61     private static final Logger log = Logger.getLogger(CapabilityOperation.class.getName());
62
63     private final CapabilityTypeOperation capabilityTypeOperation;
64     private final PropertyOperation propertyOperation;
65     
66
67     public CapabilityOperation(CapabilityTypeOperation capabilityTypeOperation, PropertyOperation propertyOperation) {
68         this.capabilityTypeOperation = capabilityTypeOperation;
69         this.propertyOperation = propertyOperation;
70     }
71     
72     
73     @VisibleForTesting
74     public void setJanusGraphGenericDao(HealingJanusGraphGenericDao janusGraphGenericDao) {
75         this.janusGraphGenericDao = janusGraphGenericDao;
76     }
77
78     public Either<CapabilityData, JanusGraphOperationStatus> addCapabilityToGraph(String resourceId, CapabilityTypeData capTypeData, CapabilityDefinition capabilityDefinition) {
79
80         log.debug("#addCapabilityToGraph - capabilityDefinition={}", capabilityDefinition);
81
82         String capUniqueId = UniqueIdBuilder.buildCapabilityUid(resourceId, capabilityDefinition.getName());
83         CapabilityData capabilityData = buildCapabilityData(capabilityDefinition, capUniqueId);
84
85         log.debug("addCapabilityToGraph - Before adding capability to graph. capabilityTypeData = {}", capabilityData);
86         Either<CapabilityData, JanusGraphOperationStatus> createCapResult = janusGraphGenericDao
87             .createNode(capabilityData, CapabilityData.class);
88         log.debug("addCapabilityToGraph - After adding capability to graph. status is = {}", createCapResult);
89
90         if (createCapResult.isRight()) {
91             JanusGraphOperationStatus operationStatus = createCapResult.right().value();
92             log.error("addCapabilityToGraph - Failed to add capability of type {} to graph. status is {}", capabilityDefinition.getType(), operationStatus);
93             return createCapResult;
94         }
95         
96         createCapResult = connectToCapabilityType(capabilityData, capTypeData)
97                                 .left()
98                                 .bind(res -> createCapabilityProperties(capabilityData, capTypeData))
99                                 .left()
100                                 .map(res -> capabilityData);
101         
102         return createCapResult;
103     }
104     
105     private Either<GraphRelation, JanusGraphOperationStatus> connectToCapabilityType(CapabilityData capabilityData, CapabilityTypeData capabilityTypeData) {
106         
107         Map<String, Object> properties = new HashMap<>();
108
109         String capabilityName = capabilityData.getCapabilityDataDefinition().getName();
110         properties.put(GraphEdgePropertiesDictionary.NAME.getProperty(), capabilityName);
111         
112         return janusGraphGenericDao
113             .createRelation(capabilityData, capabilityTypeData, GraphEdgeLabels.CAPABILITY_IMPL, properties);
114
115     }
116     
117
118     /**
119      * @param capabilites
120      * @return
121      */
122     public Either<List<CapabilityDefinition>, JanusGraphOperationStatus> getCapabilitiesWithProps(List<ImmutablePair<CapabilityData, GraphEdge>> capabilites) {
123         List<Either<CapabilityDefinition, JanusGraphOperationStatus>> listFilledCapabilitiesResults = capabilites.stream()
124                                                         .map(ImmutablePair::getLeft)
125                                                         .map(this::toCapabilityDefinitionWithProps)
126                                                         .collect(Collectors.toList());
127         
128         Optional<JanusGraphOperationStatus> status = listFilledCapabilitiesResults.stream().filter(Either::isRight)
129                                                                .map(res -> res.right().value())
130                                                                .findFirst();
131         
132         if (status.isPresent()) {
133             return Either.right(status.get());
134         }
135         
136         List<CapabilityDefinition> listCapabilities = listFilledCapabilitiesResults.stream()
137                                                                                     .map(res -> res.left().value())
138                                                                                     .collect(Collectors.toList());
139         
140         return Either.left(listCapabilities);
141     }
142     
143     private Either<CapabilityDefinition, JanusGraphOperationStatus> toCapabilityDefinitionWithProps(CapabilityData capabilityData) {
144         CapabilityDefinition capabilityDefinition = new CapabilityDefinition(capabilityData.getCapabilityDataDefinition());
145         return getCapabilityProperties(capabilityDefinition.getUniqueId(), capabilityDefinition.getType())
146                     .left()
147                     .map(props -> {
148                         capabilityDefinition.setProperties(props); 
149                         return capabilityDefinition;
150                     });
151     }
152     
153         
154     /**
155      * get all properties of the capability.
156      *
157      * the property definition is taken from the capability type.
158      *
159      * @param capabilityUid
160      * @return
161      */
162     private Either<List<ComponentInstanceProperty>, JanusGraphOperationStatus> getCapabilityProperties(String capabilityUid, String capabilityType) {
163         Either<CapabilityTypeDefinition, JanusGraphOperationStatus> capabilityTypeRes = capabilityTypeOperation.getCapabilityTypeByType(capabilityType);
164
165         if (capabilityTypeRes.isRight()) {
166             JanusGraphOperationStatus status = capabilityTypeRes.right().value();
167             return Either.right(status);
168         }
169
170         CapabilityTypeDefinition capabilityTypeDefinition = capabilityTypeRes.left().value();
171
172         Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> typesPropsRes = getPropertiesOfCapabilityTypeAndAcestors(capabilityTypeDefinition);
173         if (typesPropsRes.isRight()) {
174             JanusGraphOperationStatus status = typesPropsRes.right().value();
175             return Either.right(status);
176         }
177         
178         Map<String, PropertyDefinition> capabilityTypeProperties = typesPropsRes.left().value();
179
180         if (isEmpty(capabilityTypeProperties)) {
181             return Either.right(JanusGraphOperationStatus.OK);
182         }
183
184         Map<String, PropertyDefinition> uidToPropDefMap = capabilityTypeProperties.values().stream()
185                                                             .collect(Collectors.toMap(PropertyDefinition::getUniqueId, Function.identity()));
186
187         // Find all properties values on the capability
188         Either<List<ImmutablePair<PropertyValueData, GraphEdge>>, JanusGraphOperationStatus> propertyValNodes = janusGraphGenericDao
189             .getChildrenNodes(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Capability), capabilityUid, GraphEdgeLabels.PROPERTY_VALUE,
190                 NodeTypeEnum.PropertyValue, PropertyValueData.class);
191
192         if (propertyValNodes.isRight()) {
193             return onLoadPropValuesFailure(propertyValNodes.right().value(), capabilityTypeProperties);
194         }
195
196         List<ImmutablePair<PropertyValueData, GraphEdge>> propValsRelationPairs = propertyValNodes.left().value();
197         if (isEmpty(propValsRelationPairs)) {
198             return Either.right(JanusGraphOperationStatus.OK);
199         }
200
201         List<ComponentInstanceProperty> capabilityProperties = new ArrayList<>();
202
203         for (ImmutablePair<PropertyValueData, GraphEdge> propValRelPair : propValsRelationPairs) {
204
205             PropertyValueData propertyValueData = propValRelPair.getLeft();
206             Either<ImmutablePair<PropertyData, GraphEdge>, JanusGraphOperationStatus> propertyDefRes = janusGraphGenericDao
207                 .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.PropertyValue), propertyValueData.getUniqueId(), GraphEdgeLabels.PROPERTY_IMPL,
208                     NodeTypeEnum.Property, PropertyData.class);
209             if (propertyDefRes.isRight()) {
210                 JanusGraphOperationStatus status = propertyDefRes.right().value();
211                 if (status == JanusGraphOperationStatus.NOT_FOUND) {
212                     status = JanusGraphOperationStatus.INVALID_ID;
213                 }
214                 return Either.right(status);
215             }
216
217             ImmutablePair<PropertyData, GraphEdge> propertyDefPair = propertyDefRes.left().value();
218             PropertyData propertyData = propertyDefPair.left;
219             String propertyUniqueId = propertyData.getPropertyDataDefinition().getUniqueId();
220
221             PropertyDefinition propertyDefinition = uidToPropDefMap.get(propertyUniqueId);
222             ComponentInstanceProperty capabilityProperty = new ComponentInstanceProperty(propertyDefinition, propertyValueData.getValue(), propertyValueData.getUniqueId());
223
224             capabilityProperties.add(capabilityProperty);
225         }
226         
227         Set<String> processedProps = buildProcessedPropsSet(capabilityProperties);
228
229         // Find all properties which does not have property value on the group.
230         List<ComponentInstanceProperty> leftProps = filterCapabilityTypesProps(capabilityTypeProperties, processedProps);
231         if (leftProps != null) {
232             capabilityProperties.addAll(leftProps);
233         }
234
235         return Either.left(capabilityProperties);
236     }
237
238
239     /**
240      * @param capabilityProperties
241      * @return
242      */
243     private Set<String> buildProcessedPropsSet(List<ComponentInstanceProperty> capabilityProperties) {
244         return capabilityProperties.stream()
245                                     .map(ComponentInstanceProperty::getName)
246                                     .collect(Collectors.toSet());
247     }
248     
249     private Either<List<ComponentInstanceProperty>, JanusGraphOperationStatus> onLoadPropValuesFailure(JanusGraphOperationStatus status, Map<String, PropertyDefinition> capabilityTypeProperties) {
250         if (status == JanusGraphOperationStatus.NOT_FOUND) {
251             return Either.left(buildPropsFromCapabilityTypeProps(capabilityTypeProperties));
252         } else {
253             return Either.right(status);
254         }
255     }
256
257
258     /**
259      * @param capabilityTypeProperties
260      * @return
261      */
262     private List<ComponentInstanceProperty> buildPropsFromCapabilityTypeProps(Map<String, PropertyDefinition> capabilityTypeProperties) {
263         return capabilityTypeProperties.values().stream()
264                                                     .map(p -> new ComponentInstanceProperty(p, p.getDefaultValue(), null))
265                                                     .collect(Collectors.toList());
266     }
267
268
269     /**
270      * @param capabilityTypeRes
271      * @param capabilityTypeDefinition
272      * @return
273      */
274     private Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> getPropertiesOfCapabilityTypeAndAcestors(CapabilityTypeDefinition capabilityTypeDefinition) {
275         // Get the properties on the group type of this capability
276         Map<String, PropertyDefinition> capabilityTypeProperties = capabilityTypeDefinition.getProperties();
277         
278         String derivedFrom = capabilityTypeDefinition.getDerivedFrom();
279         if (!Strings.isNullOrEmpty(derivedFrom)) {
280             Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> parentPropsRes = capabilityTypeOperation.getAllCapabilityTypePropertiesFromAllDerivedFrom(derivedFrom);
281             if(parentPropsRes.isRight()) {
282                 JanusGraphOperationStatus status = parentPropsRes.right().value();
283                 return Either.right(status);
284             }
285             if (capabilityTypeProperties != null) {
286                 capabilityTypeProperties.putAll(parentPropsRes.left().value());
287             } else {
288                 capabilityTypeProperties = parentPropsRes.left().value();
289             }
290         }
291         
292         return Either.left(capabilityTypeProperties);
293     }
294     
295     
296     /**
297      * Create all property values of the capability and their 
298      * relations to relevant properties of the capability type.
299      *
300      * @param capabilityDefintion
301      * @param capabilityTypeData
302      * @return
303      */
304     private Either<List<ComponentInstanceProperty>, JanusGraphOperationStatus> createCapabilityProperties(CapabilityData capabilityData,
305                                                                                                           CapabilityTypeData capabilityTypeData) {
306
307         CapabilityDefinition capabilityDefintion = (CapabilityDefinition)capabilityData.getCapabilityDataDefinition();
308         CapabilityTypeDefinition capabilityTypeDefinition = (CapabilityTypeDefinition)capabilityTypeData.getCapabilityTypeDataDefinition();
309
310         Either<Map<String, PropertyDefinition>, JanusGraphOperationStatus> typesPropsRes = getPropertiesOfCapabilityTypeAndAcestors(capabilityTypeDefinition);
311         if (typesPropsRes.isRight()) {
312             JanusGraphOperationStatus status = typesPropsRes.right().value();
313             return Either.right(status);
314         }
315         
316         Map<String, PropertyDefinition> capabilityTypeProperties = typesPropsRes.left().value();
317         
318         if (isEmpty(capabilityTypeProperties) && !isEmpty(capabilityDefintion.getProperties())) {
319             log.debug("#createCapabilityProperties - It's not valid if group capability has properties while corresponding capability type doesn't.");
320             return Either.right(JanusGraphOperationStatus.MATCH_NOT_FOUND);
321         }
322
323         Optional<JanusGraphOperationStatus> error = capabilityDefintion.getProperties().stream()
324                              .map(property -> createPropertyValue(property, capabilityData, capabilityTypeProperties.get(property.getName())))
325                              .filter(Either::isRight)
326                              .map(result -> result.right().value())
327                              .findFirst();
328         if (error.isPresent()) {
329             return Either.right(error.get());
330         }
331
332         return Either.left(capabilityDefintion.getProperties());
333     }
334
335
336     /**
337      * @param capabilityTypeProperties
338      * @param excludePropsWithUniqueIds
339      * @return
340      */
341     private List<ComponentInstanceProperty> filterCapabilityTypesProps(Map<String, PropertyDefinition> capabilityTypeProperties, 
342                                                                    Set<String> excludePropsWithNames) {
343         return capabilityTypeProperties.values().stream()
344                 .filter(p -> !excludePropsWithNames.contains(p.getName()))
345                 .map(p -> new ComponentInstanceProperty(p, p.getDefaultValue(), null))
346                 .collect(Collectors.toList());
347     }
348
349     private  Either<PropertyValueData, JanusGraphOperationStatus> createPropertyValue(ComponentInstanceProperty capabilityProperty,
350                                                                                       CapabilityData capabilityData,
351                                                                                       PropertyDefinition capTypePropertyDefinition) {
352         if (capTypePropertyDefinition == null) {
353             return Either.right(JanusGraphOperationStatus.MATCH_NOT_FOUND);
354         }
355         
356         CapabilityDefinition capabilityDefintion = (CapabilityDefinition)capabilityData.getCapabilityDataDefinition();
357         
358         Either<Integer, StorageOperationStatus> indexRes = 
359                 propertyOperation.increaseAndGetObjInstancePropertyCounter(capabilityDefintion.getUniqueId(), NodeTypeEnum.Capability);
360         String uniqueId = UniqueIdBuilder.buildResourceInstancePropertyValueUid(capabilityDefintion.getUniqueId(), indexRes.left().value() );
361         PropertyValueData propertyValueData = new PropertyValueData();
362         propertyValueData.setUniqueId(uniqueId);
363         propertyValueData.setValue(capabilityProperty.getValue());
364         Either<PropertyValueData, JanusGraphOperationStatus> propResult = janusGraphGenericDao
365             .createNode(propertyValueData, PropertyValueData.class);
366         // It's not accepted if Capability Type doesn't have suitable property
367         propResult = propResult.left()
368                 .bind(propValueData -> connectToProperty(propValueData, capTypePropertyDefinition))
369                 .left()
370                 .bind(graphRelation -> connectCapability(propertyValueData, capTypePropertyDefinition.getName(), capabilityData))
371                 .left()
372                 .map(graphRelation -> propertyValueData);
373         
374         propResult.left()
375                     .foreachDoEffect(propValueData -> capabilityProperty.setUniqueId(uniqueId));
376         
377         return propResult;
378     }
379     
380     private Either<GraphRelation, JanusGraphOperationStatus> connectCapability(PropertyValueData propValueData, String name, CapabilityData capabilityData) {
381         Map<String, Object> properties = new HashMap<>();
382         properties.put(GraphEdgePropertiesDictionary.NAME.getProperty(), name);
383
384         return janusGraphGenericDao.createRelation(capabilityData, propValueData, GraphEdgeLabels.PROPERTY_VALUE, properties);
385     }
386     
387     private Either<GraphRelation, JanusGraphOperationStatus> connectToProperty(PropertyValueData propValueData, PropertyDefinition propertyDefinition) {
388         Either<PropertyData, JanusGraphOperationStatus> dataTypesRes = janusGraphGenericDao.getNode(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Property),
389                                                                             propertyDefinition.getUniqueId(), PropertyData.class);
390
391         Map<String, Object> properties = new HashMap<>();
392         properties.put(GraphEdgePropertiesDictionary.NAME.getProperty(), propertyDefinition.getName());
393         
394         return dataTypesRes.left()
395                            .bind(propertyData -> janusGraphGenericDao
396                                .createRelation(propValueData, propertyData, GraphEdgeLabels.PROPERTY_IMPL, properties));
397     }
398     
399
400     private CapabilityData buildCapabilityData(CapabilityDefinition capabilityDefinition, String ctUniqueId) {
401
402         CapabilityData capabilityData = new CapabilityData(capabilityDefinition);
403
404         capabilityData.setUniqueId(ctUniqueId);
405         Long creationDate = capabilityData.getCreationTime();
406         if (creationDate == null) {
407             creationDate = System.currentTimeMillis();
408         }
409         capabilityData.setCreationTime(creationDate);
410         capabilityData.setModificationTime(creationDate);
411         return capabilityData;
412     }
413
414
415     public StorageOperationStatus deleteCapability(CapabilityDefinition capabilityDef) {
416         
417         return janusGraphGenericDao
418             .deleteChildrenNodes(GraphPropertiesDictionary.UNIQUE_ID.getProperty(), capabilityDef.getUniqueId(), GraphEdgeLabels.PROPERTY_VALUE,
419                                                     NodeTypeEnum.PropertyValue, PropertyValueData.class)
420                  .left()
421                  .bind(props -> janusGraphGenericDao
422                      .deleteNode(new CapabilityData(capabilityDef), CapabilityData.class))
423                  .right()
424                  .map(DaoStatusConverter::convertJanusGraphStatusToStorageStatus)
425                  .right()
426                  .on(capData -> StorageOperationStatus.OK);
427     }
428     
429 }