2 * Copyright © 2016-2018 European Support Limited
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package org.openecomp.sdc.be.datamodel.utils;
19 import com.fasterxml.jackson.core.type.TypeReference;
20 import com.fasterxml.jackson.databind.ObjectMapper;
21 import fj.data.Either;
22 import org.apache.commons.collections4.CollectionUtils;
23 import org.apache.commons.collections4.ListUtils;
24 import org.apache.commons.collections4.MapUtils;
25 import org.apache.commons.lang3.ArrayUtils;
26 import org.apache.commons.lang3.StringUtils;
27 import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
28 import org.openecomp.sdc.be.dao.api.ActionStatus;
29 import org.openecomp.sdc.be.model.DataTypeDefinition;
30 import org.openecomp.sdc.be.model.InputDefinition;
31 import org.openecomp.sdc.be.model.PropertyConstraint;
32 import org.openecomp.sdc.be.model.PropertyDefinition;
33 import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
34 import org.openecomp.sdc.be.model.tosca.ToscaType;
35 import org.openecomp.sdc.be.model.tosca.constraints.ConstraintUtil;
36 import org.openecomp.sdc.be.model.tosca.constraints.ValidValuesConstraint;
37 import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
38 import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
39 import org.openecomp.sdc.exception.ResponseFormat;
40 import org.slf4j.Logger;
41 import org.slf4j.LoggerFactory;
43 import java.io.IOException;
44 import java.util.ArrayList;
45 import java.util.Collection;
46 import java.util.HashMap;
47 import java.util.List;
49 import java.util.Objects;
51 public class PropertyValueConstraintValidationUtil {
53 private static final String UNDERSCORE = "_";
54 private static final String VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY =
55 "%nValue provided in invalid format for %s property";
56 private Map<String, DataTypeDefinition> dataTypeDefinitionCache;
57 private static final Logger logger = LoggerFactory.getLogger(PropertyValueConstraintValidationUtil.class);
58 private ObjectMapper objectMapper = new ObjectMapper();
59 private List<String> errorMessages = new ArrayList<>();
60 private StringBuilder completePropertyName;
61 private String completeInputName;
62 private static final String IGNORE_PROPERTY_VALUE_START_WITH = "{\"get_input\":";
64 public static PropertyValueConstraintValidationUtil getInstance() {
65 return new PropertyValueConstraintValidationUtil();
68 public Either<Boolean, ResponseFormat> validatePropertyConstraints(
69 Collection<? extends PropertyDefinition> propertyDefinitionList,
70 ApplicationDataTypeCache applicationDataTypeCache) {
71 ResponseFormatManager responseFormatManager = getResponseFormatManager();
72 dataTypeDefinitionCache = applicationDataTypeCache.getAll().left().value();
73 CollectionUtils.emptyIfNull(propertyDefinitionList).stream()
74 .filter(this::isValuePresent)
75 .forEach(this::evaluatePropertyTypeForConstraintValidation);
77 if (CollectionUtils.isNotEmpty(errorMessages)) {
78 logger.error("Properties with Invalid Data:", errorMessages);
79 ResponseFormat inputResponse = responseFormatManager.getResponseFormat(ActionStatus
80 .INVALID_PROPERTY_VALUES, String.join(",", errorMessages));
81 return Either.right(inputResponse);
84 return Either.left(Boolean.TRUE);
87 private boolean isValuePresent(PropertyDefinition propertyDefinition) {
88 if (propertyDefinition instanceof InputDefinition) {
89 return StringUtils.isNotEmpty(propertyDefinition.getDefaultValue());
92 return StringUtils.isNotEmpty(propertyDefinition.getValue());
95 private void evaluatePropertyTypeForConstraintValidation(PropertyDefinition propertyDefinition) {
96 if (Objects.nonNull(propertyDefinition.getType())
97 && dataTypeDefinitionCache.containsKey(propertyDefinition.getType())) {
99 completeInputName = "";
100 completePropertyName = new StringBuilder();
101 if (propertyDefinition instanceof InputDefinition) {
102 completeInputName = propertyDefinition.getName();
103 propertyDefinition = getPropertyDefinitionObjectFromInputs(propertyDefinition);
106 if (Objects.nonNull(propertyDefinition)) {
107 if (ToscaType.isPrimitiveType(propertyDefinition.getType())) {
108 propertyDefinition.setConstraints(
109 org.openecomp.sdc.be.dao.utils.CollectionUtils.merge(propertyDefinition.safeGetConstraints(),
110 dataTypeDefinitionCache.get(propertyDefinition.getType()).safeGetConstraints()));
111 evaluateConstraintsOnProperty(propertyDefinition);
112 } else if (ToscaType.isCollectionType(propertyDefinition.getType())) {
113 propertyDefinition.setConstraints(
114 org.openecomp.sdc.be.dao.utils.CollectionUtils.merge(propertyDefinition.safeGetConstraints(),
115 dataTypeDefinitionCache.get(propertyDefinition.getType()).safeGetConstraints()));
116 evaluateConstraintsOnProperty(propertyDefinition);
117 evaluateCollectionTypeProperties(propertyDefinition);
119 setCompletePropertyName(propertyDefinition);
120 evaluateComplexTypeProperties(propertyDefinition);
124 errorMessages.add("\nUnsupported datatype found for property " + getCompletePropertyName(propertyDefinition));
128 private void setCompletePropertyName(PropertyDefinition propertyDefinition) {
129 if(StringUtils.isNotBlank(propertyDefinition.getUniqueId())) {
130 completePropertyName.append(
131 propertyDefinition.getUniqueId().substring(propertyDefinition.getUniqueId().lastIndexOf('.') + 1));
135 private void evaluateConstraintsOnProperty(PropertyDefinition propertyDefinition) {
136 ToscaType toscaType = ToscaType.isValidType(propertyDefinition.getType());
137 if (isPropertyNotMappedAsInput(propertyDefinition)
138 && CollectionUtils.isNotEmpty(propertyDefinition.getConstraints())
139 && isValidValueConstraintPresent(propertyDefinition.getConstraints())) {
140 for (PropertyConstraint propertyConstraint : propertyDefinition.getConstraints()) {
142 propertyConstraint.initialize(toscaType);
143 propertyConstraint.validate(toscaType, propertyDefinition.getValue());
144 } catch (ConstraintValueDoNotMatchPropertyTypeException | ConstraintViolationException exception) {
145 errorMessages.add("\n" + propertyConstraint.getErrorMessage(
146 toscaType, exception, getCompletePropertyName(propertyDefinition)));
149 } else if (isPropertyNotMappedAsInput(propertyDefinition)
150 && ToscaType.isPrimitiveType(propertyDefinition.getType())
151 && !toscaType.isValidValue(propertyDefinition.getValue())) {
152 errorMessages.add(String.format("\nUnsupported value provided for %s property supported value "
153 + "type is %s.", getCompletePropertyName(propertyDefinition), toscaType.getType()));
157 private boolean isPropertyNotMappedAsInput(PropertyDefinition propertyDefinition) {
158 return !propertyDefinition.getValue().startsWith(IGNORE_PROPERTY_VALUE_START_WITH);
161 private void checkAndEvaluatePrimitiveProperty(PropertyDefinition propertyDefinition,
162 DataTypeDefinition dataTypeDefinition) {
163 if (ToscaType.isPrimitiveType(dataTypeDefinition.getName())
164 && CollectionUtils.isNotEmpty(dataTypeDefinition.getConstraints())) {
166 PropertyDefinition definition = new PropertyDefinition();
167 definition.setValue(propertyDefinition.getValue());
168 definition.setType(dataTypeDefinition.getName());
169 definition.setConstraints(dataTypeDefinition.getConstraints());
171 evaluateConstraintsOnProperty(propertyDefinition);
175 private void evaluateComplexTypeProperties(PropertyDefinition propertyDefinition) {
176 List<PropertyDefinition> propertyDefinitions =
177 dataTypeDefinitionCache.get(propertyDefinition.getType()).getProperties();
179 Map<String, Object> valueMap =
180 MapUtils.emptyIfNull(ConstraintUtil.parseToCollection(propertyDefinition.getValue(),
181 new TypeReference<Map<String, Object>>() {}));
183 if (CollectionUtils.isEmpty(propertyDefinitions)) {
184 checkAndEvaluatePrimitiveProperty(propertyDefinition,
185 dataTypeDefinitionCache.get(propertyDefinition.getType()));
187 ListUtils.emptyIfNull(propertyDefinitions)
188 .forEach(prop -> evaluateRegularComplexType(propertyDefinition, prop, valueMap));
190 } catch (ConstraintValueDoNotMatchPropertyTypeException e) {
191 logger.debug(e.getMessage(), e);
192 errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY,
193 getCompletePropertyName(propertyDefinition)));
197 private void evaluateRegularComplexType(PropertyDefinition propertyDefinition,
198 PropertyDefinition prop,
199 Map<String, Object> valueMap) {
201 if (valueMap.containsKey(prop.getName())) {
202 if (ToscaType.isPrimitiveType(prop.getType())) {
203 evaluateConstraintsOnProperty(createPropertyDefinition(prop,
204 String.valueOf(valueMap.get(prop.getName()))));
205 } else if (ToscaType.isCollectionType(prop.getType())) {
207 evaluateCollectionTypeProperties(createPropertyDefinition(prop,
208 objectMapper.writeValueAsString(valueMap.get(prop.getName()))));
210 completePropertyName.append(UNDERSCORE);
211 completePropertyName.append(prop.getName());
212 evaluateComplexTypeProperties(
213 createPropertyDefinition(prop, objectMapper.writeValueAsString(
214 valueMap.get(prop.getName()))));
217 } catch (IOException e) {
218 logger.error(e.getMessage(), e);
219 errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY,
220 getCompletePropertyName(propertyDefinition)));
224 private void evaluateCollectionTypeProperties(PropertyDefinition propertyDefinition) {
225 ToscaType toscaPropertyType = ToscaType.isValidType(propertyDefinition.getType());
226 if (ToscaType.LIST == toscaPropertyType) {
227 evaluateListType(propertyDefinition);
228 } else if (ToscaType.MAP == toscaPropertyType) {
229 evaluateMapType(propertyDefinition);
233 private void evaluateListType(PropertyDefinition propertyDefinition) {
235 String schemaType = propertyDefinition.getSchemaType();
236 List list = ConstraintUtil.parseToCollection(propertyDefinition.getValue(),
237 new TypeReference<List<Object>>() {});
238 evaluateCollectionType(propertyDefinition, list, schemaType);
239 } catch (ConstraintValueDoNotMatchPropertyTypeException e) {
240 logger.debug(e.getMessage(), e);
241 errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY,
242 getCompletePropertyName(propertyDefinition)));
246 private void evaluateMapType(PropertyDefinition propertyDefinition) {
248 String schemaType = propertyDefinition.getSchemaType();
249 Map map = ConstraintUtil.parseToCollection(propertyDefinition.getValue(),
250 new TypeReference<Map<String, Object>>() {
252 evaluateCollectionType(propertyDefinition, map.values(), schemaType);
253 } catch (ConstraintValueDoNotMatchPropertyTypeException e) {
254 logger.debug(e.getMessage(), e);
255 errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY,
256 getCompletePropertyName(propertyDefinition)));
260 private void evaluateCollectionPrimitiveSchemaType(PropertyDefinition propertyDefinition,
263 if (Objects.nonNull(propertyDefinition.getSchema())
264 && propertyDefinition.getSchema().getProperty() instanceof PropertyDefinition) {
265 propertyDefinition.setConstraints(((PropertyDefinition) propertyDefinition.getSchema()
266 .getProperty()).getConstraints());
267 propertyDefinition.setValue(String.valueOf(value));
268 propertyDefinition.setType(schemaType);
269 evaluateConstraintsOnProperty(propertyDefinition);
272 private void evaluateCollectionType(PropertyDefinition propertyDefinition,
273 Collection valueList,
275 for (Object value : valueList) {
277 if (ToscaType.isPrimitiveType(schemaType)) {
278 evaluateCollectionPrimitiveSchemaType(propertyDefinition, value, schemaType);
279 } else if (ToscaType.isCollectionType(schemaType)) {
280 propertyDefinition.setValue(objectMapper.writeValueAsString(value));
281 propertyDefinition.setType(schemaType);
282 evaluateCollectionTypeProperties(propertyDefinition);
284 propertyDefinition.setValue(objectMapper.writeValueAsString(value));
285 propertyDefinition.setType(schemaType);
286 completePropertyName.append(UNDERSCORE);
287 completePropertyName.append(propertyDefinition.getName());
288 evaluateComplexTypeProperties(propertyDefinition);
290 } catch (IOException e) {
291 logger.debug(e.getMessage(), e);
292 errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY,
293 getCompletePropertyName(propertyDefinition)));
298 private String getCompletePropertyName(PropertyDefinition propertyDefinition) {
299 return StringUtils.isNotBlank(completeInputName) ? completeInputName :
300 StringUtils.isBlank(completePropertyName) ?
301 propertyDefinition.getName() : completePropertyName + UNDERSCORE + propertyDefinition.getName();
304 private PropertyDefinition createPropertyDefinition(PropertyDefinition prop, String value) {
305 PropertyDefinition propertyDefinition = new PropertyDefinition();
306 propertyDefinition.setName(prop.getName());
307 propertyDefinition.setValue(value);
308 propertyDefinition.setType(prop.getType());
309 propertyDefinition.setConstraints(prop.getConstraints());
310 propertyDefinition.setSchema(prop.getSchema());
312 return propertyDefinition;
315 private boolean isValidValueConstraintPresent(List<PropertyConstraint> propertyConstraints) {
316 return propertyConstraints.stream()
317 .anyMatch(propertyConstraint -> propertyConstraint instanceof ValidValuesConstraint);
320 private PropertyDefinition getPropertyDefinitionObjectFromInputs(
321 PropertyDefinition property) {
322 InputDefinition inputDefinition = (InputDefinition) property;
323 PropertyDefinition propertyDefinition = null;
325 if (CollectionUtils.isEmpty(inputDefinition.getProperties())
326 || ToscaType.isPrimitiveType(inputDefinition.getProperties().get(0).getType())) {
327 propertyDefinition = new PropertyDefinition();
328 propertyDefinition.setType(inputDefinition.getType());
329 propertyDefinition.setValue(inputDefinition.getDefaultValue());
330 propertyDefinition.setName(inputDefinition.getName());
331 } else if (Objects.nonNull(inputDefinition.getInputPath())) {
332 propertyDefinition = evaluateComplexTypeInputs(inputDefinition);
335 return propertyDefinition;
338 private PropertyDefinition evaluateComplexTypeInputs(InputDefinition inputDefinition) {
339 Map<String, Object> inputMap = new HashMap<>();
340 PropertyDefinition propertyDefinition = new PropertyDefinition();
341 String[] inputPathArr = inputDefinition.getInputPath().split("#");
342 if (inputPathArr.length > 1) {
343 inputPathArr = ArrayUtils.remove(inputPathArr, 0);
347 Map<String, Object> presentMap = inputMap;
348 for (int i = 0; i < inputPathArr.length ; i++) {
349 if (i == inputPathArr.length - 1) {
350 presentMap.computeIfAbsent(inputPathArr[i], k -> inputDefinition.getDefaultValue());
352 presentMap.computeIfAbsent(inputPathArr[i], k -> new HashMap<String, Object>());
353 presentMap = (Map<String, Object>) presentMap.get(inputPathArr[i]);
357 if (CollectionUtils.isNotEmpty(inputDefinition.getProperties())) {
358 propertyDefinition.setType(inputDefinition.getProperties().get(0).getType());
360 propertyDefinition.setName(inputDefinition.getName());
361 propertyDefinition.setValue(objectMapper.writeValueAsString(inputMap));
362 } catch (IOException e) {
363 logger.error(e.getMessage(), e);
364 errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY,
365 inputDefinition.getName()));
368 return propertyDefinition;
371 protected ResponseFormatManager getResponseFormatManager() {
372 return ResponseFormatManager.getInstance();