package alien4cloud.tosca.parser.postprocess; import static alien4cloud.utils.AlienUtils.safe; import java.util.Map; import javax.annotation.Resource; import org.springframework.stereotype.Component; import org.yaml.snakeyaml.nodes.Node; import org.alien4cloud.tosca.model.definitions.AbstractPropertyValue; import org.alien4cloud.tosca.model.definitions.FunctionPropertyValue; import org.alien4cloud.tosca.model.types.AbstractInheritableToscaType; import org.alien4cloud.tosca.model.definitions.PropertyDefinition; import org.alien4cloud.tosca.model.definitions.PropertyValue; import org.alien4cloud.tosca.model.templates.Topology; import alien4cloud.tosca.model.ArchiveRoot; import alien4cloud.tosca.parser.ParsingContextExecution; import alien4cloud.tosca.parser.ParsingError; import alien4cloud.tosca.parser.ParsingErrorLevel; import alien4cloud.tosca.parser.impl.ErrorCode; import alien4cloud.tosca.properties.constraints.exception.ConstraintValueDoNotMatchPropertyTypeException; import alien4cloud.tosca.properties.constraints.exception.ConstraintViolationException; import alien4cloud.utils.services.ConstraintPropertyService; /** * Check that property values are matching their definitions. */ @Component public class PropertyValueChecker { @Resource private ConstraintPropertyService constraintPropertyService; /** * Check that the value of a property has the right type and match constraints. * * @param type The type that defines the properties (NodeType, CapabilityType, RequirementType). * @param propertyValues The map of values. * @param templateName The name of the node template /capability template / requirement template. */ public void checkProperties(final AbstractInheritableToscaType type, final Map<String, AbstractPropertyValue> propertyValues, final String templateName) { if (type == null) { return; // if the type is null we cannot check properties against their definition. Error is managed elsewhere. } ArchiveRoot archiveRoot = (ArchiveRoot) ParsingContextExecution.getRoot().getWrappedInstance(); Topology topology = archiveRoot.getTopology(); for (Map.Entry<String, AbstractPropertyValue> propertyEntry : safe(propertyValues).entrySet()) { String propertyName = propertyEntry.getKey(); AbstractPropertyValue propertyValue = propertyEntry.getValue(); Node propertyValueNode = ParsingContextExecution.getObjectToNodeMap().get(propertyValue); if (type.getProperties() == null || !type.getProperties().containsKey(propertyName)) { ParsingContextExecution.getParsingErrors() .add(new ParsingError(ParsingErrorLevel.ERROR, ErrorCode.UNRECOGNIZED_PROPERTY, templateName, propertyValueNode.getStartMark(), "Property " + propertyName + " does not exist in type " + type.getElementId(), propertyValueNode.getEndMark(), propertyName)); continue; } PropertyDefinition propertyDefinition = type.getProperties().get(propertyName); checkProperty(propertyName, propertyValueNode, propertyValue, propertyDefinition, topology.getInputs(), templateName); } } public void checkProperty(String propertyName, Node propertyValueNode, AbstractPropertyValue propertyValue, PropertyDefinition propertyDefinition, Map<String, PropertyDefinition> inputs, String templateName) { if (propertyValue instanceof FunctionPropertyValue) { FunctionPropertyValue function = (FunctionPropertyValue) propertyValue; String parameters = function.getParameters().get(0); // check get_input only if (function.getFunction().equals("get_input")) { if (inputs == null || !inputs.keySet().contains(parameters)) { ParsingContextExecution.getParsingErrors().add(new ParsingError(ParsingErrorLevel.ERROR, ErrorCode.MISSING_TOPOLOGY_INPUT, templateName, propertyValueNode.getStartMark(), parameters, propertyValueNode.getEndMark(), propertyName)); } } } else if (propertyValue instanceof PropertyValue<?>) { checkProperty(propertyName, propertyValueNode, (PropertyValue<?>) propertyValue, propertyDefinition, templateName); } } public void checkProperty(String propertyName, Node propertyValueNode, PropertyValue<?> propertyValue, PropertyDefinition propertyDefinition, String templateName) { try { constraintPropertyService.checkPropertyConstraint(propertyName, propertyValue.getValue(), propertyDefinition, s -> ParsingContextExecution.getParsingErrors() .add(new ParsingError(ErrorCode.VALIDATION_ERROR, "A value is required but was not found for property " + s, null, "A value is required but was not found for property " + s, null, "constraints"))); } catch (ConstraintValueDoNotMatchPropertyTypeException | ConstraintViolationException e) { StringBuilder problem = new StringBuilder("Validation issue "); if (e.getConstraintInformation() != null) { problem.append("for " + e.getConstraintInformation().toString()); } problem.append(e.getMessage()); ParsingError error = null; if(propertyValueNode != null) { error = new ParsingError(ParsingErrorLevel.ERROR, ErrorCode.VALIDATION_ERROR, templateName, propertyValueNode.getStartMark(), problem.toString(), propertyValueNode.getEndMark(), propertyName); } else { error = new ParsingError(ParsingErrorLevel.ERROR, ErrorCode.VALIDATION_ERROR, templateName, null, problem.toString(), null, propertyName); } ParsingContextExecution.getParsingErrors().add(error); } } }