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