Catalog alignment
[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     public boolean isNullParam(String value) {
201         return value == null;
202     }
203
204     protected StorageOperationStatus validateAndUpdateComplexValue(IComplexDefaultValue propertyDefinition, String propertyType,
205
206             String value, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> dataTypes) {
207
208         ImmutablePair<JsonElement, Boolean> validateResult = dataTypeValidatorConverter.validateAndUpdate(value, dataTypeDefinition, dataTypes);
209
210         if (!validateResult.right.booleanValue()) {
211             log.debug("The value {} of property from type {} is invalid", propertyType, propertyType);
212             return StorageOperationStatus.INVALID_VALUE;
213         }
214
215         JsonElement jsonElement = validateResult.left;
216
217         log.trace("Going to update value in property definition {} {}" , propertyDefinition.getName() , (jsonElement != null ? jsonElement.toString() : null));
218
219         updateValue(propertyDefinition, jsonElement);
220
221         return StorageOperationStatus.OK;
222     }
223
224     protected void updateValue(IComplexDefaultValue propertyDefinition, JsonElement jsonElement) {
225
226         propertyDefinition.setDefaultValue(getValueFromJsonElement(jsonElement));
227
228     }
229
230     protected String getValueFromJsonElement(JsonElement jsonElement) {
231         String value = null;
232
233         if (jsonElement == null || jsonElement.isJsonNull()) {
234             value = EMPTY_VALUE;
235         } else {
236             value = jsonElement.toString();
237         }
238
239         return value;
240     }
241
242     protected Either<String, JanusGraphOperationStatus> getInnerType(ToscaPropertyType type, Supplier<SchemaDefinition> schemeGen) {
243         String innerType = null;
244         if (type == ToscaPropertyType.LIST || type == ToscaPropertyType.MAP) {
245
246             SchemaDefinition def = schemeGen.get();
247             if (def == null) {
248                 log.debug("Schema doesn't exists for property of type {}", type);
249                 return Either.right(JanusGraphOperationStatus.ILLEGAL_ARGUMENT);
250             }
251             PropertyDataDefinition propDef = def.getProperty();
252             if (propDef == null) {
253                 log.debug("Property in Schema Definition inside property of type {} doesn't exist", type);
254                 return Either.right(JanusGraphOperationStatus.ILLEGAL_ARGUMENT);
255             }
256             innerType = propDef.getType();
257         }
258         return Either.left(innerType);
259     }
260
261     /**
262      * Convert Constarint object to json in order to add it to the Graph
263      *
264      * @param constraints
265      * @return
266      */
267     public List<String> convertConstraintsToString(List<PropertyConstraint> constraints) {
268
269         if (constraints == null || constraints.isEmpty()) {
270             return null;
271         }
272
273         return constraints.stream().map(gson::toJson).collect(Collectors.toList());
274     }
275
276     public List<PropertyConstraint> convertConstraints(List<String> constraints) {
277
278         if (constraints == null || constraints.isEmpty()) {
279             return null;
280         }
281
282         Type constraintType = new TypeToken<PropertyConstraint>() {
283         }.getType();
284
285         Gson gson = new GsonBuilder().registerTypeAdapter(constraintType, new PropertyConstraintDeserialiser()).create();
286
287         return constraints.stream().map(c -> gson.fromJson(c, PropertyConstraint.class)).collect(Collectors.toList());
288     }
289
290 }