From d6720f15095aa5069702caa5ff40d949809d80c9 Mon Sep 17 00:00:00 2001 From: Bruno de Souza Melo Date: Wed, 11 Sep 2024 00:17:48 -0300 Subject: [PATCH] Full nullable review. Support .NET 7 removed. --- docs/Publishing.txt | 2 +- .../PasswordComplexityBaseAttribute.cs | 12 ++------ ...sswordComplexityCapitalLettersAttribute.cs | 11 ++----- .../PasswordComplexityDigitsAttribute.cs | 11 ++----- ...wordComplexityLowerCaseLettersAttribute.cs | 11 ++----- .../Annotations/StringValueBaseAttribute.cs | 8 ++--- .../Brazil/Annotations/CNPJAttribute.cs | 4 +-- .../Brazil/Annotations/CPFttribute.cs | 4 +-- src/NuvTools.Validation/Brazil/Validator.cs | 15 ++++++++++ .../NuvTools.Validation.csproj | 5 ++-- src/NuvTools.Validation/RegexPattern.cs | 15 ++++++---- src/NuvTools.Validation/Validator.cs | 28 ++++++++--------- .../Brazil/BrazilValidatorExtensions.cs | 10 +++++++ .../NuvTools.Validation.Tests.csproj | 7 ++--- .../ValidatorExtensions.cs | 30 +++++++++++++++++++ 15 files changed, 103 insertions(+), 70 deletions(-) diff --git a/docs/Publishing.txt b/docs/Publishing.txt index 0276cad..c9d1fd8 100644 --- a/docs/Publishing.txt +++ b/docs/Publishing.txt @@ -1 +1 @@ -nuget.exe push -Source "nuvtools" -ApiKey az NuvTools.Validation.8.0.0.nupkg \ No newline at end of file +nuget.exe push -Source "nuvtools" -ApiKey az NuvTools.Validation.8.1.1.nupkg \ No newline at end of file diff --git a/src/NuvTools.Validation/Annotations/PasswordComplexityBaseAttribute.cs b/src/NuvTools.Validation/Annotations/PasswordComplexityBaseAttribute.cs index 180b22e..a1f1e31 100644 --- a/src/NuvTools.Validation/Annotations/PasswordComplexityBaseAttribute.cs +++ b/src/NuvTools.Validation/Annotations/PasswordComplexityBaseAttribute.cs @@ -4,15 +4,9 @@ namespace NuvTools.Validation.Annotations; -public abstract class PasswordComplexityBaseAttribute : ValidationAttribute +public abstract class PasswordComplexityBaseAttribute(int minOccurrences, Func errorMessageAccessor) : ValidationAttribute(errorMessageAccessor) { - public int MinOccurrences { get; private set; } - - public PasswordComplexityBaseAttribute(int minOccurrences, Func errorMessageAccessor) - : base(errorMessageAccessor) - { - MinOccurrences = minOccurrences; - } + public int MinOccurrences { get; private set; } = minOccurrences; /// /// Applies formatting to a specified error message. (Overrides ) @@ -36,7 +30,7 @@ protected void EnsureLegalMinOcorrences() } } - protected static bool IsValidValue(object value) + protected static bool IsValidValue(object? value) { return value != null && value is string diff --git a/src/NuvTools.Validation/Annotations/PasswordComplexityCapitalLettersAttribute.cs b/src/NuvTools.Validation/Annotations/PasswordComplexityCapitalLettersAttribute.cs index 327b764..8f0071b 100644 --- a/src/NuvTools.Validation/Annotations/PasswordComplexityCapitalLettersAttribute.cs +++ b/src/NuvTools.Validation/Annotations/PasswordComplexityCapitalLettersAttribute.cs @@ -3,21 +3,16 @@ namespace NuvTools.Validation.Annotations; -public class PasswordComplexityCapitalLettersAttribute : PasswordComplexityBaseAttribute +public class PasswordComplexityCapitalLettersAttribute(int minOccurrences) : PasswordComplexityBaseAttribute(minOccurrences, () => Messages.XMustContainAtLeastYCapitalLetters) { - public PasswordComplexityCapitalLettersAttribute(int minOccurrences) - : base(minOccurrences, () => Messages.XMustContainAtLeastYCapitalLetters) - { - } - - public override bool IsValid(object value) + public override bool IsValid(object? value) { EnsureLegalMinOcorrences(); // Automatically pass if value is null. RequiredAttribute should be used to assert a value is not null. if (!IsValidValue(value)) return true; - string str = value as string; + string str = (string)value!; return Regex.Matches(str, @"[A-Z]").Count >= MinOccurrences; } diff --git a/src/NuvTools.Validation/Annotations/PasswordComplexityDigitsAttribute.cs b/src/NuvTools.Validation/Annotations/PasswordComplexityDigitsAttribute.cs index 9b26219..2a52888 100644 --- a/src/NuvTools.Validation/Annotations/PasswordComplexityDigitsAttribute.cs +++ b/src/NuvTools.Validation/Annotations/PasswordComplexityDigitsAttribute.cs @@ -3,21 +3,16 @@ namespace NuvTools.Validation.Annotations; -public class PasswordComplexityDigitsAttribute : PasswordComplexityBaseAttribute +public class PasswordComplexityDigitsAttribute(int minOccurrences) : PasswordComplexityBaseAttribute(minOccurrences, () => Messages.XMustContainAtLeastYDigits) { - public PasswordComplexityDigitsAttribute(int minOccurrences) - : base(minOccurrences, () => Messages.XMustContainAtLeastYDigits) - { - } - - public override bool IsValid(object value) + public override bool IsValid(object? value) { EnsureLegalMinOcorrences(); // Automatically pass if value is null. RequiredAttribute should be used to assert a value is not null. if (!IsValidValue(value)) return true; - string str = value as string; + string str = (string)value!; return Regex.Matches(str, @"[0-9]").Count >= MinOccurrences; } diff --git a/src/NuvTools.Validation/Annotations/PasswordComplexityLowerCaseLettersAttribute.cs b/src/NuvTools.Validation/Annotations/PasswordComplexityLowerCaseLettersAttribute.cs index 5653d70..be2ddc8 100644 --- a/src/NuvTools.Validation/Annotations/PasswordComplexityLowerCaseLettersAttribute.cs +++ b/src/NuvTools.Validation/Annotations/PasswordComplexityLowerCaseLettersAttribute.cs @@ -3,21 +3,16 @@ namespace NuvTools.Validation.Annotations; -public class PasswordComplexityLowerCaseLettersAttribute : PasswordComplexityBaseAttribute +public class PasswordComplexityLowerCaseLettersAttribute(int minOccurrences) : PasswordComplexityBaseAttribute(minOccurrences, () => Messages.XMustContainAtLeastYLowerCaseLetters) { - public PasswordComplexityLowerCaseLettersAttribute(int minOccurrences) - : base(minOccurrences, () => Messages.XMustContainAtLeastYLowerCaseLetters) - { - } - - public override bool IsValid(object value) + public override bool IsValid(object? value) { EnsureLegalMinOcorrences(); // Automatically pass if value is null. RequiredAttribute should be used to assert a value is not null. if (!IsValidValue(value)) return true; - string str = value as string; + string str = (string)value!; return Regex.Matches(str, @"[a-z]").Count >= MinOccurrences; } diff --git a/src/NuvTools.Validation/Annotations/StringValueBaseAttribute.cs b/src/NuvTools.Validation/Annotations/StringValueBaseAttribute.cs index 24a84ff..3968a10 100644 --- a/src/NuvTools.Validation/Annotations/StringValueBaseAttribute.cs +++ b/src/NuvTools.Validation/Annotations/StringValueBaseAttribute.cs @@ -3,12 +3,8 @@ namespace NuvTools.Validation.Annotations; -public class StringValueBaseAttribute : ValidationAttribute +public class StringValueBaseAttribute(Func errorMessageAccessor) : ValidationAttribute(errorMessageAccessor) { - public StringValueBaseAttribute(Func errorMessageAccessor) - : base(errorMessageAccessor) - { - } /// /// Applies formatting to a specified error message. (Overrides ) @@ -20,7 +16,7 @@ public override string FormatErrorMessage(string name) return string.Format(CultureInfo.CurrentCulture, ErrorMessageString, name); } - protected static bool IsValidValue(object value) + protected static bool IsValidValue(object? value) { return value != null && value is string diff --git a/src/NuvTools.Validation/Brazil/Annotations/CNPJAttribute.cs b/src/NuvTools.Validation/Brazil/Annotations/CNPJAttribute.cs index 2457687..b828f2c 100644 --- a/src/NuvTools.Validation/Brazil/Annotations/CNPJAttribute.cs +++ b/src/NuvTools.Validation/Brazil/Annotations/CNPJAttribute.cs @@ -10,12 +10,12 @@ public CNPJAttribute() { } - public override bool IsValid(object value) + public override bool IsValid(object? value) { // Automatically pass if value is null. RequiredAttribute should be used to assert a value is not null. if (!IsValidValue(value)) return true; - string str = value as string; + string str = (string)value!; return Validator.IsCNPJ(str); } diff --git a/src/NuvTools.Validation/Brazil/Annotations/CPFttribute.cs b/src/NuvTools.Validation/Brazil/Annotations/CPFttribute.cs index b231f62..9e19d82 100644 --- a/src/NuvTools.Validation/Brazil/Annotations/CPFttribute.cs +++ b/src/NuvTools.Validation/Brazil/Annotations/CPFttribute.cs @@ -10,12 +10,12 @@ public CPFAttribute() { } - public override bool IsValid(object value) + public override bool IsValid(object? value) { // Automatically pass if value is null. RequiredAttribute should be used to assert a value is not null. if (!IsValidValue(value)) return true; - string str = value as string; + string str = (string)value!; return Validator.IsCPF(str); } diff --git a/src/NuvTools.Validation/Brazil/Validator.cs b/src/NuvTools.Validation/Brazil/Validator.cs index 0d68b6d..079ded9 100644 --- a/src/NuvTools.Validation/Brazil/Validator.cs +++ b/src/NuvTools.Validation/Brazil/Validator.cs @@ -127,4 +127,19 @@ public static bool IsMobileNumber(this string mobileNumber, bool clearMask = tru return match.Success; } + /// + /// Validates Zip Code number + /// + /// Zip Code. + /// Clear mask before validate. + /// + public static bool IsZipCodeNumber(this string zipCode, bool clearMask = true) + { + zipCode = zipCode.GetNumbersOnly(); + + if (zipCode.Length != 8) return false; + + return true; + } + } \ No newline at end of file diff --git a/src/NuvTools.Validation/NuvTools.Validation.csproj b/src/NuvTools.Validation/NuvTools.Validation.csproj index 63bb4e1..ab31d71 100644 --- a/src/NuvTools.Validation/NuvTools.Validation.csproj +++ b/src/NuvTools.Validation/NuvTools.Validation.csproj @@ -1,7 +1,7 @@ - net7;net8 + net8 latest true Nuv Tools @@ -10,7 +10,7 @@ True NuvTools.Validation.snk Validation library for Web, Desktop and Mobile (MAUI) applications. - 8.1.0 + 8.1.1 True True icon.png @@ -23,6 +23,7 @@ README.md NuvTools Validators CPF CNPJ true + enable diff --git a/src/NuvTools.Validation/RegexPattern.cs b/src/NuvTools.Validation/RegexPattern.cs index fe6b019..d2fa47e 100644 --- a/src/NuvTools.Validation/RegexPattern.cs +++ b/src/NuvTools.Validation/RegexPattern.cs @@ -1,5 +1,3 @@ -using System.ComponentModel; - namespace NuvTools.Validation; /// @@ -10,10 +8,17 @@ public static class RegexPattern /// /// Regex pattern for e-mail address validation. /// - public const string EmailAddress = @"^([a-z0-9_\-])([a-z0-9_\-\.]*)@(\[((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}|((([a-z0-9\-]+)\.)+))([a-z]{2,}|(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\])$"; + public const string EMAIL_ADDRESS = @"^([a-z0-9_\-])([a-z0-9_\-\.]*)@(\[((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}|((([a-z0-9\-]+)\.)+))([a-z]{2,}|(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\])$"; + + private const string BASE64_CONTENT_OPTIONAL = "(?(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{4}))"; + + /// + /// Regex to validate Base64 and extract using content label. + /// + public const string BASE64_CONTENT = $@"^{BASE64_CONTENT_OPTIONAL}$"; /// - /// Regex to validate and extract informations (type, extension and content) from base64 file. + /// Regex to validate and extract informations (type, extension and content) from Data URI Base64. /// - public const string Base64File = @"data:(?.+?/(?.+?));(?.+),(?.+)"; + public const string BASE64_DATAURI = $@"^data:(?.+?/(?.+?));(?.+),{BASE64_CONTENT_OPTIONAL}$"; } \ No newline at end of file diff --git a/src/NuvTools.Validation/Validator.cs b/src/NuvTools.Validation/Validator.cs index 99f29d8..7b7250a 100644 --- a/src/NuvTools.Validation/Validator.cs +++ b/src/NuvTools.Validation/Validator.cs @@ -13,24 +13,22 @@ public static class Validator /// /// E-mail address /// - public static bool IsEmail(this string value) => Regex.IsMatch(value, RegexPattern.EmailAddress); + public static bool IsEmail(this string value) => Regex.IsMatch(value, RegexPattern.EMAIL_ADDRESS); - public static List Validate(this T obj) + public static List? Validate(this T obj) { + ArgumentNullException.ThrowIfNull(obj, nameof(obj)); ValidationContext context = new(obj, serviceProvider: null, items: null); - List results = new(); + List results = []; bool isValid = System.ComponentModel.DataAnnotations.Validator.TryValidateObject(obj, context, results, true); - if (isValid == false) - { - List errors = new(); - foreach (var validationResult in results) - errors.Add(validationResult.ErrorMessage); + if (isValid) return null; - return errors; - } + List errors = []; + foreach (var validationResult in results) + errors.Add(validationResult.ErrorMessage!); - return null; + return errors; } /// @@ -57,7 +55,7 @@ public static bool IsIntNumber(this string input, bool positiveOnly = false) { // Try to parse the input string as int if (int.TryParse(input, out int number)) - return positiveOnly ? int.IsPositive(number) : true; + return !positiveOnly || int.IsPositive(number); // Return false if parsing fails or number is negative return false; @@ -73,7 +71,7 @@ public static bool IsLongNumber(this string input, bool positiveOnly = false) { // Try to parse the input string as long if (long.TryParse(input, out long number)) - return positiveOnly ? long.IsPositive(number) : true; + return !positiveOnly || long.IsPositive(number); // Return false if parsing fails or number is negative return false; @@ -86,11 +84,11 @@ public static bool IsLongNumber(this string input, bool positiveOnly = false) /// If only positive numbers is valid. /// An object that supplies culture-specific parsing information about s. /// True if the input is a valid decimal number, otherwise false. - public static bool IsDecimalNumber(this string input, bool positiveOnly = false, IFormatProvider provider = null) + public static bool IsDecimalNumber(this string input, bool positiveOnly = false, IFormatProvider? provider = null) { // Try to parse the input string as a decimal if (decimal.TryParse(input, System.Globalization.NumberStyles.Number | System.Globalization.NumberStyles.AllowDecimalPoint, provider, out decimal number)) - return positiveOnly ? decimal.IsPositive(number) : true; + return !positiveOnly || decimal.IsPositive(number); return false; } diff --git a/tests/NuvTools.Validation.Test/Brazil/BrazilValidatorExtensions.cs b/tests/NuvTools.Validation.Test/Brazil/BrazilValidatorExtensions.cs index 3e1143b..6fd87ed 100644 --- a/tests/NuvTools.Validation.Test/Brazil/BrazilValidatorExtensions.cs +++ b/tests/NuvTools.Validation.Test/Brazil/BrazilValidatorExtensions.cs @@ -36,4 +36,14 @@ public void ValidateMobileNumber() Assert.That(!"21866664444".IsMobileNumber()); } + [Test()] + public void ValidateZipCode() + { + Assert.That("71065-100".IsZipCodeNumber()); + Assert.That("88999232".IsZipCodeNumber()); + Assert.That(!"(21) 95555-7777".IsZipCodeNumber()); + Assert.That(!"95.555-777".IsZipCodeNumber()); + Assert.That(!"error".IsZipCodeNumber()); + } + } \ No newline at end of file diff --git a/tests/NuvTools.Validation.Test/NuvTools.Validation.Tests.csproj b/tests/NuvTools.Validation.Test/NuvTools.Validation.Tests.csproj index 9509b5f..fa65907 100644 --- a/tests/NuvTools.Validation.Test/NuvTools.Validation.Tests.csproj +++ b/tests/NuvTools.Validation.Test/NuvTools.Validation.Tests.csproj @@ -3,7 +3,6 @@ net8.0 false - 8.1.0 Nuv Tools Copyright © 2024 Nuv Tools https://nuvtools.com @@ -12,9 +11,9 @@ - - - + + + diff --git a/tests/NuvTools.Validation.Test/ValidatorExtensions.cs b/tests/NuvTools.Validation.Test/ValidatorExtensions.cs index 35c1ec5..9dba655 100644 --- a/tests/NuvTools.Validation.Test/ValidatorExtensions.cs +++ b/tests/NuvTools.Validation.Test/ValidatorExtensions.cs @@ -1,5 +1,6 @@ using NUnit.Framework; using System.Globalization; +using System.Text.RegularExpressions; namespace NuvTools.Validation.Tests; @@ -47,4 +48,33 @@ public void ValidateIsDecimalNumber() Assert.That("83289988074".IsDecimalNumber()); } + + [Test()] + public void ValidateDataURIBase64() + { + string dataURIBase64 = "data:text/plain;base64,TnV2IFRvb2xz"; + + Regex regex = new(RegexPattern.BASE64_DATAURI); + var result = regex.Match(dataURIBase64); + + Assert.That(result.Groups["type"].Value == "text/plain"); + Assert.That(result.Groups["extension"].Value == "plain"); + Assert.That(result.Groups["content"].Value == "TnV2IFRvb2xz"); + } + + [Test()] + public void ValidateBase64() + { + string base64Content = "TnV2IFRvb2xz"; + + Regex regex = new(RegexPattern.BASE64_CONTENT); + var result = regex.Match(base64Content); + + Assert.That(result.Groups["content"].Value == "TnV2IFRvb2xz"); + + var restultB = regex.IsMatch(base64Content + "A"); + + Assert.That(!restultB); + } + } \ No newline at end of file