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