private ValidatorDefinition ExtractValidatorDefinition(Type type) { var rules = typeValidationRuleBuilder.Build(type, ""); if (!rules.Any()) { return(null); } var ruleset = rules.Aggregate((a, b) => a + "; " + b); return(new ValidatorDefinition(DataApiClient.GetCollectionName(type), ValidatorType.TextRules, ruleset)); }
public List <string> Build(PropertyInfo property, string parentPath) { var propertyRules = new List <string>(); var propertyName = property.Name; var propertyPath = string.IsNullOrEmpty(parentPath) ? propertyName : $"{parentPath}.{propertyName}"; var propertyType = property.PropertyType; var dataTypeRule = BuildDataTypeRule(propertyPath, propertyType); if (dataTypeRule != null) { propertyRules.Add(dataTypeRule); } var validationAttributes = property.GetCustomAttributes <ValidationAttribute>().ToList(); var isOptional = !validationAttributes.Select(x => x.GetType()) .Intersect( new[] { typeof(RequiredAttribute), typeof(RequiredIfPropertyEqualToAttribute), typeof(RequiredIfPropertyNotEqualToAttribute) }).Any(); foreach (var validationAttribute in validationAttributes) { string rule = null; switch (validationAttribute) { case ItemCountAttribute itemCountAttribute: rule = BuildItemCountRule(propertyPath, itemCountAttribute); break; case LessThanAttribute lessThanAttribute: rule = BuildLessThan(propertyPath, lessThanAttribute); break; case NotEmptyAttribute notEmptyAttribute: rule = BuildNotEmptyRule(propertyPath); break; case RangeOrNaNAttribute rangeOrNaNAttribute: rule = BuildRangeRule(propertyPath, rangeOrNaNAttribute, allowNaN: true); break; case RequiredIfPropertyEqualToAttribute requiredIfPropertyHasValueAttribute: rule = BuildRequiredIfPropertyHasValueRule(propertyPath, requiredIfPropertyHasValueAttribute); break; case RequiredIfPropertyNotEqualToAttribute requiredIfPropertyNotHasValueAttribute: rule = BuildRequiredIfPropertyNotHasValueRule(propertyPath, requiredIfPropertyNotHasValueAttribute); break; case DataReferenceAttribute dataReferenceAttribute: rule = BuildDataReferenceRule(propertyPath, dataReferenceAttribute); break; case GreaterThanAttribute greaterThanAttribute: rule = BuildGreaterThan(propertyPath, greaterThanAttribute); break; case EmailAddressAttribute emailAddressAttribute: rule = BuildRegularExpressionRule(propertyPath, new RegularExpressionAttribute("^[a-zA-Z0-9\\._-]+@[a-zA-Z0-9\\._-]+\\.[a-zA-Z0-9]+$")); break; case EnumDataTypeAttribute enumDataTypeAttribute: if (enumDataTypeAttribute.CustomDataType != null) { propertyType = Assembly.GetAssembly(typeof(IId)).GetTypes() .Where(t => t.IsEnum) .SingleOrDefault(t => t.Name == enumDataTypeAttribute.CustomDataType); } else { throw new NotSupportedException(); } break; case UrlAttribute urlAttribute: rule = BuildRegularExpressionRule(propertyPath, new RegularExpressionAttribute("^https?://.+$")); break; case DataTypeAttribute dataTypeAttribute: if (dataTypeAttribute.CustomDataType != null) { propertyType = Assembly.GetAssembly(typeof(IId)).GetTypes() .SingleOrDefault(t => t.Name == dataTypeAttribute.CustomDataType); } else { throw new NotSupportedException(); } break; case MaxLengthAttribute maxLengthAttribute: if (propertyType == typeof(string)) { rule = BuildStringLengthRule(propertyPath, new StringLengthAttribute(maxLengthAttribute.Length)); } else if (propertyType.IsInstanceOfType(typeof(IList))) { rule = BuildItemCountRule(propertyPath, new ItemCountAttribute(0, maxLengthAttribute.Length)); } else { throw new NotSupportedException(); } break; case MinLengthAttribute minLengthAttribute: if (propertyType == typeof(string)) { rule = BuildMinimumStringLengthRule(propertyPath, minLengthAttribute); } else if (propertyType.IsInstanceOfType(typeof(IList))) { rule = BuildMinimumItemsCountRule(propertyPath, minLengthAttribute); } else { throw new NotSupportedException(); } break; case RangeAttribute rangeAttribute: rule = BuildRangeRule(propertyPath, rangeAttribute); break; case RegularExpressionAttribute regularExpressionAttribute: rule = BuildRegularExpressionRule(propertyPath, regularExpressionAttribute); break; case RequiredAttribute requiredAttribute: rule = BuildExistsRule(propertyPath); break; case StringLengthAttribute stringLengthAttribute: rule = BuildStringLengthRule(propertyPath, stringLengthAttribute); break; } if (rule == null) { continue; } if (isOptional) { rule += $" IF {BuildExistsRule(propertyPath)}"; } propertyRules.Add(rule); } // Recurse to child-properties if (propertyType != null && !propertyType.IsPrimitive && propertyPath.Count(c => c == '.') + 1 < maximumPathDepth) { var stringSerializedTypes = new [] { typeof(string), typeof(DateTime), typeof(DateTime?), typeof(TimeSpan), typeof(TimeSpan?), typeof(UnitValue) }; if (typeof(IDictionary).IsAssignableFrom(propertyType)) { // Do nothing } else if (typeof(IList).IsAssignableFrom(propertyType)) { var itemType = propertyType.IsArray ? propertyType.GetElementType() : propertyType.GenericTypeArguments.SingleOrDefault(); if (itemType != null && !itemType.InSet(stringSerializedTypes)) { propertyRules.AddRange(typeValidationRuleBuilder.Build(itemType, propertyPath)); } } else if (propertyType.InSet(stringSerializedTypes)) // These are serialized to string without further hierarchy { // Go no deeper } else { foreach (var childProperty in property.PropertyType.GetProperties()) { propertyRules.AddRange(Build(childProperty, propertyPath)); } } } return(propertyRules); }