Support TOSCA functions in Node Filters
[sdc.git] / catalog-model / src / main / java / org / openecomp / sdc / be / model / tosca / validators / DataTypeValidatorConverter.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.tosca.validators;
21
22 import com.google.gson.JsonElement;
23 import com.google.gson.JsonObject;
24 import com.google.gson.JsonParser;
25 import com.google.gson.JsonPrimitive;
26 import com.google.gson.JsonSyntaxException;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30 import java.util.Map.Entry;
31 import java.util.Set;
32 import lombok.AccessLevel;
33 import lombok.NoArgsConstructor;
34 import org.apache.commons.lang3.tuple.ImmutablePair;
35 import org.openecomp.sdc.be.model.DataTypeDefinition;
36 import org.openecomp.sdc.be.model.PropertyDefinition;
37 import org.openecomp.sdc.be.model.tosca.ToscaPropertyType;
38 import org.openecomp.sdc.be.model.tosca.converters.PropertyValueConverter;
39 import org.openecomp.sdc.common.log.wrappers.Logger;
40
41 @NoArgsConstructor(access = AccessLevel.PRIVATE)
42 public class DataTypeValidatorConverter {
43
44     private static final Logger log = Logger.getLogger(DataTypeValidatorConverter.class.getName());
45     private static final DataTypeValidatorConverter dataTypeValidatorConverter = new DataTypeValidatorConverter();
46     private static final ImmutablePair<JsonElement, Boolean> falseResult = new ImmutablePair<>(null, false);
47     private static final ImmutablePair<JsonElement, Boolean> trueEmptyResult = new ImmutablePair<>(null, true);
48
49     public static DataTypeValidatorConverter getInstance() {
50         return dataTypeValidatorConverter;
51     }
52
53     private ToscaPropertyType isDataTypeDerviedFromScalarType(DataTypeDefinition dataTypeDef) {
54         ToscaPropertyType result = null;
55         DataTypeDefinition dataType = dataTypeDef;
56         while (dataType != null) {
57             String name = dataType.getName();
58             ToscaPropertyType typeIfScalar = ToscaPropertyType.getTypeIfScalar(name);
59             if (typeIfScalar != null) {
60                 result = typeIfScalar;
61                 break;
62             }
63             dataType = dataType.getDerivedFrom();
64         }
65         return result;
66     }
67
68     private ImmutablePair<JsonElement, Boolean> validateAndUpdate(JsonElement jsonElement, DataTypeDefinition dataTypeDefinition,
69                                                                   Map<String, DataTypeDefinition> allDataTypes) {
70         Map<String, PropertyDefinition> allProperties = getAllProperties(dataTypeDefinition);
71         ToscaPropertyType toscaPropertyType = null;
72         if ((toscaPropertyType = isDataTypeDerviedFromScalarType(dataTypeDefinition)) != null) {
73             PropertyTypeValidator validator = toscaPropertyType.getValidator();
74             PropertyValueConverter converter = toscaPropertyType.getConverter();
75             if (jsonElement == null || jsonElement.isJsonNull()) {
76                 boolean valid = validator.isValid(null, null, allDataTypes);
77                 if (!valid) {
78                     log.trace("Failed in validation of property {} from type {}", dataTypeDefinition.getName(), dataTypeDefinition.getName());
79                     return falseResult;
80                 }
81                 return new ImmutablePair<>(jsonElement, true);
82             } else {
83                 if (jsonElement.isJsonPrimitive()) {
84                     String value = null;
85                     if (jsonElement != null) {
86                         if (jsonElement.toString().isEmpty()) {
87                             value = "";
88                         } else {
89                             value = jsonElement.toString();
90                         }
91                     }
92                     boolean valid = validator.isValid(value, null, null);
93                     if (!valid) {
94                         log.trace("Failed in validation of property {} from type {}. Json primitive value is {}", dataTypeDefinition.getName(),
95                             dataTypeDefinition.getName(), value);
96                         return falseResult;
97                     }
98                     String convertedValue = converter.convert(value, null, allDataTypes);
99                     JsonElement element = null;
100                     try {
101                         element = JsonParser.parseString(convertedValue);
102                     } catch (JsonSyntaxException e) {
103                         log.debug("Failed to parse value {} of property {} {}", convertedValue, dataTypeDefinition.getName(), e);
104                         return falseResult;
105                     }
106                     return new ImmutablePair<>(element, true);
107                 } else {
108                     // MAP, LIST, OTHER types cannot be applied data type
109
110                     // definition scalar type. We currently cannot derived from
111
112                     // map/list. (cannot add the entry schema to it)
113                     log.debug("We cannot derive from list/map. Thus, the value cannot be not primitive since the data type {} is scalar one",
114                         dataTypeDefinition.getName());
115                     return falseResult;
116                 }
117             }
118         } else {
119             if (jsonElement == null || jsonElement.isJsonNull()) {
120                 return new ImmutablePair<>(jsonElement, true);
121             } else {
122                 if (jsonElement.isJsonObject()) {
123                     JsonObject buildJsonObject = new JsonObject();
124                     JsonObject asJsonObject = jsonElement.getAsJsonObject();
125                     Set<Entry<String, JsonElement>> entrySet = asJsonObject.entrySet();
126                     for (Entry<String, JsonElement> entry : entrySet) {
127                         String propName = entry.getKey();
128                         JsonElement elementValue = entry.getValue();
129                         PropertyDefinition propertyDefinition = allProperties.get(propName);
130                         if (propertyDefinition == null) {
131                             log.debug("The property {} was not found under data type {}", propName, dataTypeDefinition.getName());
132                             return falseResult;
133                         }
134                         String type = propertyDefinition.getType();
135                         boolean isScalarType = ToscaPropertyType.isScalarType(type);
136                         if (isScalarType) {
137                             ToscaPropertyType propertyType = ToscaPropertyType.isValidType(type);
138                             if (propertyType == null) {
139                                 log.debug("cannot find the {} under default tosca property types", type);
140                                 return falseResult;
141                             }
142                             PropertyTypeValidator validator = propertyType.getValidator();
143                             String innerType = null;
144                             if (propertyType == ToscaPropertyType.LIST || propertyType == ToscaPropertyType.MAP) {
145                                 if (propertyDefinition.getSchema() != null && propertyDefinition.getSchema().getProperty() != null) {
146                                     innerType = propertyDefinition.getSchema().getProperty().getType();
147                                     if (innerType == null) {
148                                         log.debug("Property type {} must have inner type in its declaration.", propertyType);
149                                         return falseResult;
150                                     }
151                                 }
152                             }
153                             String value = null;
154                             if (elementValue != null) {
155                                 if (elementValue.isJsonPrimitive() && elementValue.getAsString().isEmpty()) {
156                                     value = "";
157                                 } else {
158                                     value = elementValue.toString();
159                                 }
160                             }
161                             boolean isValid = validator.isValid(value, innerType, allDataTypes);
162                             if (!isValid) {
163                                 log.debug("Failed to validate the value {} from type {}", value, propertyType);
164                                 return falseResult;
165                             }
166                             PropertyValueConverter converter = propertyType.getConverter();
167                             String convertedValue = converter.convert(value, innerType, allDataTypes);
168                             JsonElement element = null;
169                             if (convertedValue != null) {
170                                 if (convertedValue.isEmpty()) {
171                                     element = new JsonPrimitive("");
172                                 } else {
173                                     try {
174                                         element = JsonParser.parseString(convertedValue);
175                                     } catch (JsonSyntaxException e) {
176                                         log.debug("Failed to parse value {} of type {}", convertedValue, propertyType, e);
177                                         return falseResult;
178                                     }
179                                 }
180                             }
181                             buildJsonObject.add(propName, element);
182                         } else {
183                             DataTypeDefinition typeDefinition = allDataTypes.get(type);
184                             if (typeDefinition == null) {
185                                 log.debug("The data type {} cannot be found in the given data type list.", type);
186                                 return falseResult;
187                             }
188                             ImmutablePair<JsonElement, Boolean> isValid = validateAndUpdate(elementValue, typeDefinition, allDataTypes);
189                             if (!isValid.getRight().booleanValue()) {
190                                 log.debug("Failed in validation of value {} from type {}", (elementValue != null ? elementValue.toString() : null),
191                                     typeDefinition.getName());
192                                 return falseResult;
193                             }
194                             buildJsonObject.add(propName, isValid.getLeft());
195                         }
196                     }
197                     return new ImmutablePair<>(buildJsonObject, true);
198                 } else {
199                     log.debug("The value {} of type {} should be json object", (jsonElement != null ? jsonElement.toString() : null),
200                         dataTypeDefinition.getName());
201                     return falseResult;
202                 }
203             }
204         }
205     }
206
207     public ImmutablePair<JsonElement, Boolean> validateAndUpdate(String value, DataTypeDefinition dataTypeDefinition,
208                                                                  Map<String, DataTypeDefinition> allDataTypes) {
209         if (value == null || value.isEmpty()) {
210             return trueEmptyResult;
211         }
212         final JsonElement jsonElement;
213         try {
214             jsonElement = JsonParser.parseString(value);
215         } catch (JsonSyntaxException e) {
216             return falseResult;
217         }
218         return validateAndUpdate(jsonElement, dataTypeDefinition, allDataTypes);
219     }
220
221     private Map<String, PropertyDefinition> getAllProperties(DataTypeDefinition dataTypeDefinition) {
222         Map<String, PropertyDefinition> allParentsProps = new HashMap<>();
223         while (dataTypeDefinition != null) {
224             List<PropertyDefinition> currentParentsProps = dataTypeDefinition.getProperties();
225             if (currentParentsProps != null) {
226                 currentParentsProps.stream().forEach(p -> allParentsProps.put(p.getName(), p));
227             }
228             dataTypeDefinition = dataTypeDefinition.getDerivedFrom();
229         }
230         return allParentsProps;
231     }
232
233     public boolean isValid(String value, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> allDataTypes) {
234         if (value == null || value.isEmpty()) {
235             return true;
236         }
237         JsonElement jsonElement;
238         try {
239             jsonElement = JsonParser.parseString(value);
240         } catch (JsonSyntaxException e) {
241             log.debug("Failed to parse the value {} from type {}", value, dataTypeDefinition, e);
242             return false;
243         }
244         return isValid(jsonElement, dataTypeDefinition, allDataTypes);
245     }
246
247     private boolean isValid(JsonElement jsonElement, DataTypeDefinition dataTypeDefinition, Map<String, DataTypeDefinition> allDataTypes) {
248         Map<String, PropertyDefinition> allProperties = getAllProperties(dataTypeDefinition);
249         ToscaPropertyType toscaPropertyType = null;
250         if ((toscaPropertyType = isDataTypeDerviedFromScalarType(dataTypeDefinition)) != null) {
251             PropertyTypeValidator validator = toscaPropertyType.getValidator();
252             if (jsonElement == null || jsonElement.isJsonNull()) {
253                 boolean valid = validator.isValid(null, null, allDataTypes);
254                 if (!valid) {
255                     log.trace("Failed in validation of property {} from type {}", dataTypeDefinition.getName(), dataTypeDefinition.getName());
256                     return false;
257                 }
258                 return true;
259             } else {
260                 if (jsonElement.isJsonPrimitive()) {
261                     String value = null;
262                     if (jsonElement != null) {
263                         if (jsonElement.toString().isEmpty()) {
264                             value = "";
265                         } else {
266                             value = jsonElement.toString();
267                         }
268                     }
269                     boolean valid = validator.isValid(value, null, allDataTypes);
270                     if (!valid) {
271                         log.trace("Failed in validation of property {} from type {}. Json primitive value is {}", dataTypeDefinition.getName(),
272                             dataTypeDefinition.getName(), value);
273                         return false;
274                     }
275                     return true;
276                 } else {
277                     // MAP, LIST, OTHER types cannot be applied data type
278
279                     // definition scalar type. We currently cannot derived from
280
281                     // map/list. (cannot add the entry schema to it)
282                     log.debug("We cannot derive from list/map. Thus, the value cannot be not primitive since the data type {} is scalar one",
283                         dataTypeDefinition.getName());
284                     return false;
285                 }
286             }
287         } else {
288             if (jsonElement == null || jsonElement.isJsonNull()) {
289                 return true;
290             } else {
291                 if (jsonElement.isJsonObject()) {
292                     JsonObject asJsonObject = jsonElement.getAsJsonObject();
293                     Set<Entry<String, JsonElement>> entrySet = asJsonObject.entrySet();
294                     for (Entry<String, JsonElement> entry : entrySet) {
295                         String propName = entry.getKey();
296                         JsonElement elementValue = entry.getValue();
297                         PropertyDefinition propertyDefinition = allProperties.get(propName);
298                         if (propertyDefinition == null) {
299                             log.debug("The property {} was not found under data type {}", propName, dataTypeDefinition.getName());
300                             return false;
301                         }
302                         String type = propertyDefinition.getType();
303                         boolean isScalarType = ToscaPropertyType.isScalarType(type);
304                         if (isScalarType) {
305                             ToscaPropertyType propertyType = ToscaPropertyType.isValidType(type);
306                             if (propertyType == null) {
307                                 log.debug("cannot find the {} under default tosca property types", type);
308                                 return false;
309                             }
310                             PropertyTypeValidator validator = propertyType.getValidator();
311                             String innerType = null;
312                             if (propertyType == ToscaPropertyType.LIST || propertyType == ToscaPropertyType.MAP) {
313                                 if (propertyDefinition.getSchema() != null && propertyDefinition.getSchema().getProperty() != null) {
314                                     innerType = propertyDefinition.getSchema().getProperty().getType();
315                                     if (innerType == null) {
316                                         log.debug("Property type {} must have inner type in its declaration.", propertyType);
317                                         return false;
318                                     }
319                                 }
320                             }
321                             String value = null;
322                             if (elementValue != null) {
323                                 if (elementValue.isJsonPrimitive() && elementValue.getAsString().isEmpty()) {
324                                     value = "";
325                                 } else {
326                                     value = elementValue.toString();
327                                 }
328                             }
329                             boolean isValid = validator.isValid(value, innerType, allDataTypes);
330                             if (!isValid) {
331                                 log.debug("Failed to validate the value {} from type {}", value, propertyType);
332                                 return false;
333                             }
334                         } else {
335                             DataTypeDefinition typeDefinition = allDataTypes.get(type);
336                             if (typeDefinition == null) {
337                                 log.debug("The data type {} cannot be found in the given data type list.", type);
338                                 return false;
339                             }
340                             boolean isValid = isValid(elementValue, typeDefinition, allDataTypes);
341                             if (!isValid) {
342                                 log.debug("Failed in validation of value {} from type {}", (elementValue != null ? elementValue.toString() : null),
343                                     typeDefinition.getName());
344                                 return false;
345                             }
346                         }
347                     }
348                     return true;
349                 } else {
350                     log.debug("The value {} of type {} should be json object", (jsonElement != null ? jsonElement.toString() : null),
351                         dataTypeDefinition.getName());
352                     return false;
353                 }
354             }
355         }
356     }
357 }