Improve test coverage
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / operations / impl / AbstractOperation.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.gson.Gson;
24 import com.google.gson.GsonBuilder;
25 import com.google.gson.JsonElement;
26 import com.google.gson.reflect.TypeToken;
27 import fj.data.Either;
28 import java.lang.reflect.Type;
29 import java.util.List;
30 import java.util.Map;
31 import java.util.function.Supplier;
32 import java.util.stream.Collectors;
33 import org.apache.commons.lang3.tuple.ImmutablePair;
34 import org.openecomp.sdc.be.config.BeEcompErrorManager;
35 import org.openecomp.sdc.be.config.BeEcompErrorManager.ErrorSeverity;
36 import org.openecomp.sdc.be.dao.graph.datatype.GraphEdge;
37 import org.openecomp.sdc.be.dao.janusgraph.HealingJanusGraphGenericDao;
38 import org.openecomp.sdc.be.dao.janusgraph.JanusGraphOperationStatus;
39 import org.openecomp.sdc.be.dao.neo4j.GraphEdgeLabels;
40 import org.openecomp.sdc.be.datatypes.elements.PropertyDataDefinition;
41 import org.openecomp.sdc.be.datatypes.elements.SchemaDefinition;
42 import org.openecomp.sdc.be.datatypes.enums.NodeTypeEnum;
43 import org.openecomp.sdc.be.model.DataTypeDefinition;
44 import org.openecomp.sdc.be.model.IComplexDefaultValue;
45 import org.openecomp.sdc.be.model.PropertyConstraint;
46 import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
47 import org.openecomp.sdc.be.model.operations.api.StorageOperationStatus;
48 import org.openecomp.sdc.be.model.operations.impl.PropertyOperation.PropertyConstraintDeserialiser;
49 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
50 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
51 import org.openecomp.sdc.be.model.tosca.validators.DataTypeValidatorConverter;
52 import org.openecomp.sdc.be.model.tosca.validators.PropertyTypeValidator;
53 import org.openecomp.sdc.be.resources.data.ResourceMetadataData;
54 import org.openecomp.sdc.common.log.wrappers.Logger;
55 import org.springframework.beans.factory.annotation.Autowired;
56
57 public abstract class AbstractOperation {
58
59     private static final Logger log = Logger.getLogger(AbstractOperation.class.getName());
60
61     @Autowired
62     protected HealingJanusGraphGenericDao janusGraphGenericDao;
63
64     public static final String EMPTY_VALUE = null;
65
66     protected Gson gson = new Gson();
67
68     @Autowired
69     protected ApplicationDataTypeCache applicationDataTypeCache;
70
71     protected DataTypeValidatorConverter dataTypeValidatorConverter = DataTypeValidatorConverter.getInstance();
72
73     interface NodeElementFetcher<ElementDefinition> {
74         JanusGraphOperationStatus findAllNodeElements(String nodeId, List<ElementDefinition> listTofill);
75     }
76
77     public <ElementDefinition> JanusGraphOperationStatus findAllResourceElementsDefinitionRecursively(String resourceId, List<ElementDefinition> elements, NodeElementFetcher<ElementDefinition> singleNodeFetcher) {
78
79         if (log.isTraceEnabled())
80             log.trace("Going to fetch elements under resource {}", resourceId);
81         JanusGraphOperationStatus
82             resourceAttributesStatus = singleNodeFetcher.findAllNodeElements(resourceId, elements);
83
84         if (resourceAttributesStatus != JanusGraphOperationStatus.OK) {
85             return resourceAttributesStatus;
86         }
87
88         Either<ImmutablePair<ResourceMetadataData, GraphEdge>, JanusGraphOperationStatus> parentNodes = janusGraphGenericDao
89             .getChild(UniqueIdBuilder.getKeyByNodeType(NodeTypeEnum.Resource), resourceId, GraphEdgeLabels.DERIVED_FROM, NodeTypeEnum.Resource,
90                 ResourceMetadataData.class);
91
92         if (parentNodes.isRight()) {
93             JanusGraphOperationStatus parentNodesStatus = parentNodes.right().value();
94             if (parentNodesStatus != JanusGraphOperationStatus.NOT_FOUND) {
95                 BeEcompErrorManager.getInstance().logInternalFlowError("findAllResourceElementsDefinitionRecursively", "Failed to find parent elements of resource " + resourceId + ". status is " + parentNodesStatus, ErrorSeverity.ERROR);
96                 return parentNodesStatus;
97             }
98         }
99
100         if (parentNodes.isLeft()) {
101             ImmutablePair<ResourceMetadataData, GraphEdge> parnetNodePair = parentNodes.left().value();
102             String parentUniqueId = parnetNodePair.getKey().getMetadataDataDefinition().getUniqueId();
103             JanusGraphOperationStatus addParentIntStatus = findAllResourceElementsDefinitionRecursively(parentUniqueId, elements, singleNodeFetcher);
104
105             if (addParentIntStatus != JanusGraphOperationStatus.OK) {
106                 BeEcompErrorManager.getInstance().logInternalFlowError("findAllResourceElementsDefinitionRecursively", "Failed to find all resource elements of resource " + parentUniqueId, ErrorSeverity.ERROR);
107
108                 return addParentIntStatus;
109             }
110         }
111         return JanusGraphOperationStatus.OK;
112     }
113
114     protected <T, TStatus> void handleTransactionCommitRollback(boolean inTransaction, Either<T, TStatus> result) {
115         if (!inTransaction) {
116             if (result == null || result.isRight()) {
117                 log.error("Going to execute rollback on graph.");
118                 janusGraphGenericDao.rollback();
119             } else {
120                 log.debug("Going to execute commit on graph.");
121                 janusGraphGenericDao.commit();
122             }
123         }
124     }
125
126
127     /**
128      * @param propertyDefinition
129      * @return
130      */
131
132     protected StorageOperationStatus validateAndUpdateProperty(IComplexDefaultValue propertyDefinition, Map<String, DataTypeDefinition> dataTypes) {
133
134         log.trace("Going to validate property type and value. {}", propertyDefinition);
135
136         String propertyType = propertyDefinition.getType();
137         String value = propertyDefinition.getDefaultValue();
138
139         ToscaPropertyType type = getType(propertyType);
140
141         if (type == null) {
142
143             DataTypeDefinition dataTypeDefinition = dataTypes.get(propertyType);
144             if (dataTypeDefinition == null) {
145                 log.debug("The type {}  of property cannot be found.", propertyType);
146                 return StorageOperationStatus.INVALID_TYPE;
147             }
148
149             return validateAndUpdateComplexValue(propertyDefinition, propertyType, value, dataTypeDefinition, dataTypes);
150
151         }
152         String innerType = null;
153
154         Either<String, JanusGraphOperationStatus> checkInnerType = getInnerType(type, propertyDefinition::getSchema);
155         if (checkInnerType.isRight()) {
156             return StorageOperationStatus.INVALID_TYPE;
157         }
158         innerType = checkInnerType.left().value();
159
160         log.trace("After validating property type {}", propertyType);
161
162         boolean isValidProperty = isValidValue(type, value, innerType, dataTypes);
163         if (!isValidProperty) {
164             log.info("The value {} of property from type {} is invalid", value, type);
165             return StorageOperationStatus.INVALID_VALUE;
166         }
167
168         PropertyValueConverter converter = type.getConverter();
169
170         if (isEmptyValue(value)) {
171             log.debug("Default value was not sent for property {}. Set default value to {}", propertyDefinition.getName(), EMPTY_VALUE);
172             propertyDefinition.setDefaultValue(EMPTY_VALUE);
173         } else if (!isEmptyValue(value)) {
174             String convertedValue = converter.convert(value, innerType, dataTypes);
175             propertyDefinition.setDefaultValue(convertedValue);
176         }
177         return StorageOperationStatus.OK;
178     }
179
180     protected ToscaPropertyType getType(String propertyType) {
181
182         return ToscaPropertyType.isValidType(propertyType);
183
184     }
185
186     protected boolean isValidValue(ToscaPropertyType type, String value, String innerType, Map<String, DataTypeDefinition> dataTypes) {
187         if (isEmptyValue(value)) {
188             return true;
189         }
190
191         PropertyTypeValidator validator = type.getValidator();
192
193         return validator.isValid(value, innerType, dataTypes);
194     }
195
196     public boolean isEmptyValue(String value) {
197         return value == null;
198     }
199
200     protected StorageOperationStatus validateAndUpdateComplexValue(IComplexDefaultValue propertyDefinition, String propertyType,
201
202             String value, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> dataTypes) {
203
204         ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
205
206         if (!validateResult.right.booleanValue()) {
207             log.debug("The value {} of property from type {} is invalid", propertyType, propertyType);
208             return StorageOperationStatus.INVALID_VALUE;
209         }
210
211         JsonElement jsonElement = validateResult.left;
212
213         log.trace("Going to update value in property definition {} {}" , propertyDefinition.getName() , (jsonElement != null ? jsonElement.toString() : null));
214
215         updateValue(propertyDefinition, jsonElement);
216
217         return StorageOperationStatus.OK;
218     }
219
220     protected void updateValue(IComplexDefaultValue propertyDefinition, JsonElement jsonElement) {
221
222         propertyDefinition.setDefaultValue(getValueFromJsonElement(jsonElement));
223
224     }
225
226     protected String getValueFromJsonElement(JsonElement jsonElement) {
227         if (jsonElement == null || jsonElement.isJsonNull()) {
228             return EMPTY_VALUE;
229         } else {
230             return jsonElement.toString();
231         }
232     }
233
234     protected Either<String, JanusGraphOperationStatus> getInnerType(ToscaPropertyType type, Supplier<SchemaDefinition> schemeGen) {
235         String innerType = null;
236         if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
237
238             SchemaDefinition def = schemeGen.get();
239             if (def == null) {
240                 log.debug("Schema doesn't exists for property of type {}", type);
241                 return Either.right(JanusGraphOperationStatus.ILLEGAL_ARGUMENT);
242             }
243             PropertyDataDefinition propDef = def.getProperty();
244             if (propDef == null) {
245                 log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
246                 return Either.right(JanusGraphOperationStatus.ILLEGAL_ARGUMENT);
247             }
248             innerType = propDef.getType();
249         }
250         return Either.left(innerType);
251     }
252
253     /**
254      * Convert Constarint object to json in order to add it to the Graph
255      *
256      * @param constraints
257      * @return
258      */
259     public List<String> convertConstraintsToString(List<PropertyConstraint> constraints) {
260
261         if (constraints == null || constraints.isEmpty()) {
262             return null;
263         }
264
265         return constraints.stream().map(gson::toJson).collect(Collectors.toList());
266     }
267
268     public List<PropertyConstraint> convertConstraints(List<String> constraints) {
269
270         if (constraints == null || constraints.isEmpty()) {
271             return null;
272         }
273
274         Type constraintType = new TypeToken<PropertyConstraint>() {
275         }.getType();
276
277         Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintDeserialiser()).create();
278
279         return constraints.stream().map(c -> gson.fromJson(c, PropertyConstraint.class)).collect(Collectors.toList());
280     }
281
282 }