Skip to content

Commit

Permalink
feat: added AcceptCountryRule to restrict validation to a specific …
Browse files Browse the repository at this point in the history
…set of countries.
  • Loading branch information
skwasjer committed Dec 31, 2021
1 parent 57e4385 commit 5bc8359
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 3 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
# Changelog

## v5.3.3
## v5.4.0

- Use new `Chunk()` LINQ API for .NET 6 and change own `Partition()` method into polyfill for older framework targets.
- Added `AcceptCountryRule` to restrict validation to a specific set of countries. The rule can be added via the validator options or dependency registration extensions.

## v5.3.2

Expand Down
23 changes: 21 additions & 2 deletions src/IbanNet/Resources.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions src/IbanNet/Resources.resx
Original file line number Diff line number Diff line change
Expand Up @@ -179,4 +179,10 @@
<data name="Exception_The_country_0_does_not_define_a_IBAN_pattern" xml:space="preserve">
<value>The country '{0}' does not define a IBAN pattern.</value>
</data>
<data name="CountryNotAcceptedResult_Bank_account_numbers_from_country_0_are_not_accepted" xml:space="preserve">
<value>Bank account numbers from country {0} are not accepted.</value>
</data>
<data name="ArgumentException_At_least_one_country_code_must_be_provided" xml:space="preserve">
<value>At least one country code must be provided.</value>
</data>
</root>
16 changes: 16 additions & 0 deletions src/IbanNet/Validation/Results/CountryNotAcceptedResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using IbanNet.Registry;

namespace IbanNet.Validation.Results;

/// <inheritdoc />
public class CountryNotAcceptedResult : ErrorResult
{
/// <summary>
/// The result returned when the IBAN is not accepted because of restrictions by country.
/// </summary>
/// <param name="country">The country that was rejected.</param>
public CountryNotAcceptedResult(IbanCountry country)
: base(string.Format(Resources.CountryNotAcceptedResult_Bank_account_numbers_from_country_0_are_not_accepted, country.DisplayName))
{
}
}
39 changes: 39 additions & 0 deletions src/IbanNet/Validation/Rules/AcceptCountryRule.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using IbanNet.Validation.Results;

namespace IbanNet.Validation.Rules;

/// <summary>
/// A validation rule that accepts only specific countries.
/// The rule can be used if your use case needs a limitation on countries to allow.
/// </summary>
/// <remarks>Returns <see cref="CountryNotAcceptedResult" /> on validation failures.</remarks>
public class AcceptCountryRule : IIbanValidationRule
{
private readonly ISet<string> _acceptedCountryCodes;

/// <summary>
/// Initializes a new instance of the <see cref="AcceptCountryRule" /> using specified <paramref name="acceptedCountryCodes" />.
/// </summary>
/// <param name="acceptedCountryCodes">An enumerable of accepted country codes (2 letter ISO region name)</param>
public AcceptCountryRule(IEnumerable<string> acceptedCountryCodes)
{
if (acceptedCountryCodes is null)
{
throw new ArgumentNullException(nameof(acceptedCountryCodes));
}

_acceptedCountryCodes = new HashSet<string>(acceptedCountryCodes.Select(cc => cc.ToUpperInvariant()), StringComparer.Ordinal);
if (_acceptedCountryCodes.Count == 0)
{
throw new ArgumentException(Resources.ArgumentException_At_least_one_country_code_must_be_provided, nameof(acceptedCountryCodes));
}
}

/// <inheritdoc />
public ValidationRuleResult Validate(ValidationRuleContext context)
{
return _acceptedCountryCodes.Contains(context.Country!.TwoLetterISORegionName)
? ValidationRuleResult.Success
: new CountryNotAcceptedResult(context.Country);
}
}
72 changes: 72 additions & 0 deletions test/IbanNet.Tests/Validation/Rules/AcceptCountryRuleTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
using IbanNet.Registry;
using IbanNet.Validation.Results;

namespace IbanNet.Validation.Rules;

public class AcceptCountryRuleTests
{
[Theory]
[InlineData("NL91ABNA0417164300")]
[InlineData("BE68539007547034")]
public void Given_that_country_code_is_accepted_when_validating_it_should_return_success(string value)
{
var sut = new AcceptCountryRule(new[] { "NL", "BE" });
IbanCountry ibanCountry = IbanRegistry.Default[value.Substring(0, 2)];

// Act
ValidationRuleResult actual = sut.Validate(new ValidationRuleContext(value, ibanCountry));

// Assert
actual.Should().Be(ValidationRuleResult.Success);
}

[Theory]
[InlineData("NL91ABNA0417164300")]
[InlineData("BE68539007547034")]
public void Given_that_country_code_is_not_accepted_when_validating_it_should_return_error(string value)
{
var sut = new AcceptCountryRule(new[] { "DE", "FR" });
IbanCountry ibanCountry = IbanRegistry.Default[value.Substring(0, 2)];

// Act
ValidationRuleResult actual = sut.Validate(new ValidationRuleContext(value, ibanCountry));

// Assert
actual.Should()
.BeOfType<CountryNotAcceptedResult>()
.Which.ErrorMessage.Should()
.Be(string.Format(Resources.CountryNotAcceptedResult_Bank_account_numbers_from_country_0_are_not_accepted, ibanCountry.DisplayName));
}

[Fact]
public void Given_that_list_is_null_when_creating_rule_it_should_throw()
{
IEnumerable<string> acceptedCountryCodes = null;

// Act
// ReSharper disable once AssignNullToNotNullAttribute
Func<AcceptCountryRule> act = () => new AcceptCountryRule(acceptedCountryCodes);

// Assert
act.Should()
.ThrowExactly<ArgumentNullException>()
.Which.ParamName.Should()
.Be(nameof(acceptedCountryCodes));
}

[Fact]
public void Given_that_list_is_empty_when_creating_rule_it_should_throw()
{
IEnumerable<string> acceptedCountryCodes = Enumerable.Empty<string>();

// Act
Func<AcceptCountryRule> act = () => new AcceptCountryRule(acceptedCountryCodes);

// Assert
act.Should()
.ThrowExactly<ArgumentException>()
.WithMessage(Resources.ArgumentException_At_least_one_country_code_must_be_provided + "*")
.Which.ParamName.Should()
.Be(nameof(acceptedCountryCodes));
}
}

0 comments on commit 5bc8359

Please sign in to comment.