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.
16 package org.openecomp.sdc.be.datamodel.utils;
18 import com.fasterxml.jackson.core.type.TypeReference;
19 import com.fasterxml.jackson.databind.ObjectMapper;
20 import fj.data.Either;
21 import java.io.IOException;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.HashMap;
25 import java.util.List;
27 import java.util.Objects;
28 import org.apache.commons.collections4.CollectionUtils;
29 import org.apache.commons.collections4.ListUtils;
30 import org.apache.commons.collections4.MapUtils;
31 import org.apache.commons.lang3.ArrayUtils;
32 import org.apache.commons.lang3.StringUtils;
33 import org.openecomp.sdc.be.components.impl.ResponseFormatManager;
34 import org.openecomp.sdc.be.dao.api.ActionStatus;
35 import org.openecomp.sdc.be.model.DataTypeDefinition;
36 import org.openecomp.sdc.be.model.InputDefinition;
37 import org.openecomp.sdc.be.model.PropertyConstraint;
38 import org.openecomp.sdc.be.model.PropertyDefinition;
39 import org.openecomp.sdc.be.model.cache.ApplicationDataTypeCache;
40 import org.openecomp.sdc.be.model.tosca.ToscaType;
41 import org.openecomp.sdc.be.model.tosca.constraints.ConstraintUtil;
42 import org.openecomp.sdc.be.model.tosca.constraints.ValidValuesConstraint;
43 import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException;
44 import org.openecomp.sdc.be.model.tosca.constraints.exception.ConstraintViolationException;
45 import org.openecomp.sdc.exception.ResponseFormat;
46 import org.slf4j.Logger;
47 import org.slf4j.LoggerFactory;
49 public class PropertyValueConstraintValidationUtil {
51 private static final String UNDERSCORE = "_";
52 private static final String VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY = "%nValue provided in invalid format for %s property";
53 private static final Logger logger = LoggerFactory.getLogger(PropertyValueConstraintValidationUtil.class);
54 private static final String IGNORE_PROPERTY_VALUE_START_WITH = "{\"get_input\":";
55 private Map<String, DataTypeDefinition> dataTypeDefinitionCache;
56 private ObjectMapper objectMapper = new ObjectMapper();
57 private List<String> errorMessages = new ArrayList<>();
58 private StringBuilder completePropertyName;
59 private String completeInputName;
61 public static PropertyValueConstraintValidationUtil getInstance() {
62 return new PropertyValueConstraintValidationUtil();
65 public Either<Boolean, ResponseFormat> validatePropertyConstraints(final Collection<? extends PropertyDefinition> propertyDefinitionList,
66 final ApplicationDataTypeCache applicationDataTypeCache,
68 ResponseFormatManager responseFormatManager = getResponseFormatManager();
69 dataTypeDefinitionCache = applicationDataTypeCache.getAll(model).left().value();
70 CollectionUtils.emptyIfNull(propertyDefinitionList).stream().filter(this::isValuePresent)
71 .forEach(this::evaluatePropertyTypeForConstraintValidation);
72 if (CollectionUtils.isNotEmpty(errorMessages)) {
73 logger.error("Properties with Invalid Data:", errorMessages);
74 ResponseFormat inputResponse = responseFormatManager
75 .getResponseFormat(ActionStatus.INVALID_PROPERTY_VALUES, String.join(",", errorMessages));
76 return Either.right(inputResponse);
78 return Either.left(Boolean.TRUE);
81 private boolean isValuePresent(PropertyDefinition propertyDefinition) {
82 if (propertyDefinition instanceof InputDefinition) {
83 return StringUtils.isNotEmpty(propertyDefinition.getDefaultValue());
85 return StringUtils.isNotEmpty(propertyDefinition.getValue());
88 private void evaluatePropertyTypeForConstraintValidation(PropertyDefinition propertyDefinition) {
89 if (Objects.nonNull(propertyDefinition.getType()) && dataTypeDefinitionCache.containsKey(propertyDefinition.getType())) {
90 completeInputName = "";
91 completePropertyName = new StringBuilder();
92 if (propertyDefinition instanceof InputDefinition) {
93 completeInputName = propertyDefinition.getName();
94 propertyDefinition = getPropertyDefinitionObjectFromInputs(propertyDefinition);
96 if (Objects.nonNull(propertyDefinition)) {
97 if (ToscaType.isPrimitiveType(propertyDefinition.getType())) {
98 propertyDefinition.setConstraints(org.openecomp.sdc.be.dao.utils.CollectionUtils.merge(propertyDefinition.safeGetConstraints(),
99 dataTypeDefinitionCache.get(propertyDefinition.getType()).safeGetConstraints()));
100 evaluateConstraintsOnProperty(propertyDefinition);
101 } else if (ToscaType.isCollectionType(propertyDefinition.getType())) {
102 propertyDefinition.setConstraints(org.openecomp.sdc.be.dao.utils.CollectionUtils.merge(propertyDefinition.safeGetConstraints(),
103 dataTypeDefinitionCache.get(propertyDefinition.getType()).safeGetConstraints()));
104 evaluateConstraintsOnProperty(propertyDefinition);
105 evaluateCollectionTypeProperties(propertyDefinition);
107 setCompletePropertyName(propertyDefinition);
108 evaluateComplexTypeProperties(propertyDefinition);
112 errorMessages.add("\nUnsupported datatype found for property " + getCompletePropertyName(propertyDefinition));
116 private void setCompletePropertyName(PropertyDefinition propertyDefinition) {
117 if (StringUtils.isNotBlank(propertyDefinition.getUniqueId())) {
118 completePropertyName.append(propertyDefinition.getUniqueId().substring(propertyDefinition.getUniqueId().lastIndexOf('.') + 1));
122 private void evaluateConstraintsOnProperty(PropertyDefinition propertyDefinition) {
123 ToscaType toscaType = ToscaType.isValidType(propertyDefinition.getType());
124 if (isPropertyNotMappedAsInput(propertyDefinition) && CollectionUtils.isNotEmpty(propertyDefinition.getConstraints())
125 && isValidValueConstraintPresent(propertyDefinition.getConstraints())) {
126 for (PropertyConstraint propertyConstraint : propertyDefinition.getConstraints()) {
128 propertyConstraint.initialize(toscaType);
129 propertyConstraint.validate(toscaType, propertyDefinition.getValue());
130 } catch (ConstraintValueDoNotMatchPropertyTypeException | ConstraintViolationException exception) {
131 errorMessages.add("\n" + propertyConstraint.getErrorMessage(toscaType, exception, getCompletePropertyName(propertyDefinition)));
134 } else if (isPropertyNotMappedAsInput(propertyDefinition) && ToscaType.isPrimitiveType(propertyDefinition.getType()) && !toscaType
135 .isValidValue(propertyDefinition.getValue())) {
136 errorMessages.add(String
137 .format("\nUnsupported value provided for %s property supported value " + "type is %s.", getCompletePropertyName(propertyDefinition),
138 toscaType.getType()));
142 private boolean isPropertyNotMappedAsInput(PropertyDefinition propertyDefinition) {
143 return !propertyDefinition.getValue().startsWith(IGNORE_PROPERTY_VALUE_START_WITH);
146 private void checkAndEvaluatePrimitiveProperty(PropertyDefinition propertyDefinition, DataTypeDefinition dataTypeDefinition) {
147 if (ToscaType.isPrimitiveType(dataTypeDefinition.getName()) && CollectionUtils.isNotEmpty(dataTypeDefinition.getConstraints())) {
148 PropertyDefinition definition = new PropertyDefinition();
149 definition.setValue(propertyDefinition.getValue());
150 definition.setType(dataTypeDefinition.getName());
151 definition.setConstraints(dataTypeDefinition.getConstraints());
152 evaluateConstraintsOnProperty(propertyDefinition);
156 private void evaluateComplexTypeProperties(PropertyDefinition propertyDefinition) {
157 List<PropertyDefinition> propertyDefinitions = dataTypeDefinitionCache.get(propertyDefinition.getType()).getProperties();
159 Map<String, Object> valueMap = MapUtils
160 .emptyIfNull(ConstraintUtil.parseToCollection(propertyDefinition.getValue(), new TypeReference<Map<String, Object>>() {
162 if (CollectionUtils.isEmpty(propertyDefinitions)) {
163 checkAndEvaluatePrimitiveProperty(propertyDefinition, dataTypeDefinitionCache.get(propertyDefinition.getType()));
165 ListUtils.emptyIfNull(propertyDefinitions).forEach(prop -> evaluateRegularComplexType(propertyDefinition, prop, valueMap));
167 } catch (ConstraintValueDoNotMatchPropertyTypeException e) {
168 logger.debug(e.getMessage(), e);
169 errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY, getCompletePropertyName(propertyDefinition)));
173 private void evaluateRegularComplexType(PropertyDefinition propertyDefinition, PropertyDefinition prop, Map<String, Object> valueMap) {
175 if (valueMap.containsKey(prop.getName())) {
176 if (ToscaType.isPrimitiveType(prop.getType())) {
177 evaluateConstraintsOnProperty(createPropertyDefinition(prop, String.valueOf(valueMap.get(prop.getName()))));
178 } else if (ToscaType.isCollectionType(prop.getType())) {
179 evaluateCollectionTypeProperties(createPropertyDefinition(prop, objectMapper.writeValueAsString(valueMap.get(prop.getName()))));
181 completePropertyName.append(UNDERSCORE);
182 completePropertyName.append(prop.getName());
183 evaluateComplexTypeProperties(createPropertyDefinition(prop, objectMapper.writeValueAsString(valueMap.get(prop.getName()))));
186 } catch (IOException e) {
187 logger.error(e.getMessage(), e);
188 errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY, getCompletePropertyName(propertyDefinition)));
192 private void evaluateCollectionTypeProperties(PropertyDefinition propertyDefinition) {
193 ToscaType toscaPropertyType = ToscaType.isValidType(propertyDefinition.getType());
194 if (ToscaType.LIST == toscaPropertyType) {
195 evaluateListType(propertyDefinition);
196 } else if (ToscaType.MAP == toscaPropertyType) {
197 evaluateMapType(propertyDefinition);
201 private void evaluateListType(PropertyDefinition propertyDefinition) {
203 String schemaType = propertyDefinition.getSchemaType();
204 List list = ConstraintUtil.parseToCollection(propertyDefinition.getValue(), new TypeReference<List<Object>>() {
206 evaluateCollectionType(propertyDefinition, list, schemaType);
207 } catch (ConstraintValueDoNotMatchPropertyTypeException e) {
208 logger.debug(e.getMessage(), e);
209 errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY, getCompletePropertyName(propertyDefinition)));
213 private void evaluateMapType(PropertyDefinition propertyDefinition) {
215 String schemaType = propertyDefinition.getSchemaType();
216 Map map = ConstraintUtil.parseToCollection(propertyDefinition.getValue(), new TypeReference<Map<String, Object>>() {
218 evaluateCollectionType(propertyDefinition, map.values(), schemaType);
219 } catch (ConstraintValueDoNotMatchPropertyTypeException e) {
220 logger.debug(e.getMessage(), e);
221 errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY, getCompletePropertyName(propertyDefinition)));
225 private void evaluateCollectionPrimitiveSchemaType(PropertyDefinition propertyDefinition, Object value, String schemaType) {
226 if (Objects.nonNull(propertyDefinition.getSchema()) && propertyDefinition.getSchema().getProperty() instanceof PropertyDefinition) {
227 propertyDefinition.setConstraints(((PropertyDefinition) propertyDefinition.getSchema().getProperty()).getConstraints());
228 propertyDefinition.setValue(String.valueOf(value));
229 propertyDefinition.setType(schemaType);
230 evaluateConstraintsOnProperty(propertyDefinition);
234 private void evaluateCollectionType(PropertyDefinition propertyDefinition, Collection valueList, String schemaType) {
235 for (Object value : valueList) {
237 if (ToscaType.isPrimitiveType(schemaType)) {
238 evaluateCollectionPrimitiveSchemaType(propertyDefinition, value, schemaType);
239 } else if (ToscaType.isCollectionType(schemaType)) {
240 propertyDefinition.setValue(objectMapper.writeValueAsString(value));
241 propertyDefinition.setType(schemaType);
242 evaluateCollectionTypeProperties(propertyDefinition);
244 propertyDefinition.setValue(objectMapper.writeValueAsString(value));
245 propertyDefinition.setType(schemaType);
246 completePropertyName.append(UNDERSCORE);
247 completePropertyName.append(propertyDefinition.getName());
248 evaluateComplexTypeProperties(propertyDefinition);
250 } catch (IOException e) {
251 logger.debug(e.getMessage(), e);
252 errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY, getCompletePropertyName(propertyDefinition)));
257 private String getCompletePropertyName(PropertyDefinition propertyDefinition) {
258 return StringUtils.isNotBlank(completeInputName) ? completeInputName
259 : StringUtils.isBlank(completePropertyName) ? propertyDefinition.getName()
260 : completePropertyName + UNDERSCORE + propertyDefinition.getName();
263 private PropertyDefinition createPropertyDefinition(PropertyDefinition prop, String value) {
264 PropertyDefinition propertyDefinition = new PropertyDefinition();
265 propertyDefinition.setName(prop.getName());
266 propertyDefinition.setValue(value);
267 propertyDefinition.setType(prop.getType());
268 propertyDefinition.setConstraints(prop.getConstraints());
269 propertyDefinition.setSchema(prop.getSchema());
270 return propertyDefinition;
273 private boolean isValidValueConstraintPresent(List<PropertyConstraint> propertyConstraints) {
274 return propertyConstraints.stream().anyMatch(propertyConstraint -> propertyConstraint instanceof ValidValuesConstraint);
277 private PropertyDefinition getPropertyDefinitionObjectFromInputs(PropertyDefinition property) {
278 InputDefinition inputDefinition = (InputDefinition) property;
279 PropertyDefinition propertyDefinition = null;
280 if (CollectionUtils.isEmpty(inputDefinition.getProperties()) || ToscaType.isPrimitiveType(inputDefinition.getProperties().get(0).getType())) {
281 propertyDefinition = new PropertyDefinition();
282 propertyDefinition.setType(inputDefinition.getType());
283 propertyDefinition.setValue(inputDefinition.getDefaultValue());
284 propertyDefinition.setName(inputDefinition.getName());
285 } else if (Objects.nonNull(inputDefinition.getInputPath())) {
286 propertyDefinition = evaluateComplexTypeInputs(inputDefinition);
288 return propertyDefinition;
291 private PropertyDefinition evaluateComplexTypeInputs(InputDefinition inputDefinition) {
292 Map<String, Object> inputMap = new HashMap<>();
293 PropertyDefinition propertyDefinition = new PropertyDefinition();
294 String[] inputPathArr = inputDefinition.getInputPath().split("#");
295 if (inputPathArr.length > 1) {
296 inputPathArr = ArrayUtils.remove(inputPathArr, 0);
299 Map<String, Object> presentMap = inputMap;
300 for (int i = 0; i < inputPathArr.length; i++) {
301 if (i == inputPathArr.length - 1) {
302 presentMap.computeIfAbsent(inputPathArr[i], k -> inputDefinition.getDefaultValue());
304 presentMap.computeIfAbsent(inputPathArr[i], k -> new HashMap<String, Object>());
305 presentMap = (Map<String, Object>) presentMap.get(inputPathArr[i]);
308 if (CollectionUtils.isNotEmpty(inputDefinition.getProperties())) {
309 propertyDefinition.setType(inputDefinition.getProperties().get(0).getType());
311 propertyDefinition.setName(inputDefinition.getName());
312 propertyDefinition.setValue(objectMapper.writeValueAsString(inputMap));
313 } catch (IOException e) {
314 logger.error(e.getMessage(), e);
315 errorMessages.add(String.format(VALUE_PROVIDED_IN_INVALID_FORMAT_FOR_PROPERTY, inputDefinition.getName()));
317 return propertyDefinition;
320 protected ResponseFormatManager getResponseFormatManager() {
321 return ResponseFormatManager.getInstance();