diff --git a/Validation/AlphanumericAttribute.cs b/Validation/AlphanumericAttribute.cs new file mode 100644 index 0000000..58ce175 --- /dev/null +++ b/Validation/AlphanumericAttribute.cs @@ -0,0 +1,13 @@ + +using System.ComponentModel.DataAnnotations; +using System.Numerics; +using System.Text; +using System.Text.RegularExpressions; + +namespace NejCommon.Validation; + +public class AlphanumericAttribute : RegexAttribute{ + public AlphanumericAttribute() : base("^[a-zA-Z0-9]*$","Aabb123"){ + + } +} \ No newline at end of file diff --git a/Validation/IbanAttribute.cs b/Validation/IbanAttribute.cs new file mode 100644 index 0000000..f8a188e --- /dev/null +++ b/Validation/IbanAttribute.cs @@ -0,0 +1,50 @@ + +using System.ComponentModel.DataAnnotations; +using System.Numerics; +using System.Text; +using System.Text.RegularExpressions; + +namespace NejCommon.Validation; + +public class IbanAttribute : RegexAttribute{ + public const string IbanRegex = @"^[A-Z]{2}\d{2}[A-Z0-9]{11,30}$"; + public IbanAttribute() : base(IbanRegex,"CZ4622500000000104816258"){ + + } + + protected override ValidationResult IsValid(object value, ValidationContext ctx) + { + if (value is null) + return ValidationResult.Success!; + var res = base.IsValid(value, ctx); + if (res != ValidationResult.Success) + return res; + + string iban = value as string; + + string rearranged = iban.Substring(4) + iban.Substring(0, 4); + + // Convert letters to numbers (A=10, B=11, ..., Z=35) + StringBuilder numericIban = new StringBuilder(); + foreach (char ch in rearranged) + { + if (char.IsLetter(ch)) + { + int charVal = ch - 'A' + 10; + numericIban.Append(charVal); + } + else + { + numericIban.Append(ch); + } + } + + // Convert to BigInteger and compute mod 97 + BigInteger ibanNumber = BigInteger.Parse(numericIban.ToString()); + + if(ibanNumber % 97 == 1) + return ValidationResult.Success; + else + return new ValidationResult("Invalid checksum of IBAN"); + } +} \ No newline at end of file diff --git a/Validation/NejValidationAttribute.cs b/Validation/NejValidationAttribute.cs new file mode 100644 index 0000000..cc96731 --- /dev/null +++ b/Validation/NejValidationAttribute.cs @@ -0,0 +1,19 @@ + + +using System.ComponentModel.DataAnnotations; +using Microsoft.OpenApi.Models; + +namespace NejCommon.Validation; + +[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)] +public abstract class NejValidationAttribute : ValidationAttribute +{ + public NejValidationAttribute(string errorMsg = null) + { + ErrorMessage = errorMsg ?? $"Invalid format"; + } + + public abstract string ExampleJson { get; } + + public virtual void ApplySchema(OpenApiSchema schema) { } +} \ No newline at end of file diff --git a/Validation/NejValidationAttributeFilter.cs b/Validation/NejValidationAttributeFilter.cs new file mode 100644 index 0000000..f19bb98 --- /dev/null +++ b/Validation/NejValidationAttributeFilter.cs @@ -0,0 +1,42 @@ +using Microsoft.OpenApi.Models; +using Swashbuckle.AspNetCore.SwaggerGen; + +namespace NejCommon.Validation; + +public class NejValidationAttributeFilter : ISchemaFilter +{ + public void Apply(OpenApiSchema schema, SchemaFilterContext context) + { + var type = context.Type; + var attrOnType = context.MemberInfo?.GetCustomAttributes(typeof(NejValidationAttribute), true)?.Select(a => a as NejValidationAttribute)?.FirstOrDefault(); + if (attrOnType != null) + { + Console.WriteLine("Added examples for: " + context.MemberInfo?.Name); + schema.Example = OpenApiAnyFactory.CreateFromJson(attrOnType.ExampleJson); + attrOnType.ApplySchema(schema); + } + + var attrOnType2 = context.ParameterInfo?.GetCustomAttributes(typeof(NejValidationAttribute), true)?.Select(a => a as NejValidationAttribute)?.FirstOrDefault(); + if (attrOnType2 != null) + { + Console.WriteLine("Added examples for: " + context.ParameterInfo?.Name); + schema.Example = OpenApiAnyFactory.CreateFromJson(attrOnType2.ExampleJson); + attrOnType.ApplySchema(schema); + } + + foreach (var prop in type.GetProperties()) + { + if (schema.Properties.TryGetValue(prop.Name, out var pschema)) + { + Console.WriteLine("Schema for: " + prop.Name); + var attr = prop.GetCustomAttributes(typeof(NejValidationAttribute), true).Select(a => a as NejValidationAttribute).FirstOrDefault(); + if (attr != null) + { + Console.WriteLine("Added examples for: " + prop.Name); + pschema.Example = OpenApiAnyFactory.CreateFromJson(attr.ExampleJson); + attrOnType.ApplySchema(schema); + } + } + } + } +} \ No newline at end of file diff --git a/Validation/RegexAttribute.cs b/Validation/RegexAttribute.cs new file mode 100644 index 0000000..9b75f56 --- /dev/null +++ b/Validation/RegexAttribute.cs @@ -0,0 +1,36 @@ + +using System.ComponentModel.DataAnnotations; +using System.Text.Json; +using System.Text.RegularExpressions; +using Microsoft.OpenApi.Models; + +namespace NejCommon.Validation; + +public class RegexAttribute : NejValidationAttribute +{ + public override string ExampleJson { get; } + public string Pattern { get; } + public RegexAttribute(string pattern, string example, string errorMsg = null) + { + Pattern = pattern; + ExampleJson = JsonSerializer.Serialize(example); + ErrorMessage = errorMsg ?? $"Invalid format (must match {pattern})"; + } + + protected override ValidationResult IsValid(object value, ValidationContext ctx) + { + if (value is null) + return ValidationResult.Success!; + + var str = value as string; + if (string.IsNullOrWhiteSpace(str) || !Regex.IsMatch(str, Pattern)) + return new ValidationResult(ErrorMessage); + return ValidationResult.Success!; + } + + public override void ApplySchema(OpenApiSchema schema) + { + base.ApplySchema(schema); + schema.Pattern = Pattern; + } +} \ No newline at end of file