Skip to content

Commit

Permalink
Merge pull request #3 from deveel/validation-error-handling
Browse files Browse the repository at this point in the history
Improvements to the Validation Error Handling
  • Loading branch information
tsutomi authored Mar 1, 2025
2 parents fac5069 + 1c16d2b commit 17d8748
Show file tree
Hide file tree
Showing 8 changed files with 221 additions and 37 deletions.
28 changes: 22 additions & 6 deletions .github/workflows/cicd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
strategy:
fail-fast: false
matrix:
dotnet-version: [6.0.x,7.0.x,8.0.x]
dotnet-version: [6.0.x,7.0.x,8.0.x,9.0.x]

steps:
- name: Checkout Code
Expand All @@ -26,15 +26,31 @@ jobs:
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet-version }}


- name: Emit .NET 6.0 Framework Moniker
if: matrix.dotnet-version == '6.0.x'
run: echo "DOTNET_FX_VERSION=net6.0" >> $GITHUB_ENV

- name: Emit .NET 7.0 Framework Moniker
if: matrix.dotnet-version == '7.0.x'
run: echo "DOTNET_FX_VERSION=net7.0" >> $GITHUB_ENV

- name: Emit .NET 8.0 Framework Moniker
if: matrix.dotnet-version == '8.0.x'
run: echo "DOTNET_FX_VERSION=net8.0" >> $GITHUB_ENV

- name: Emit .NET 9.0 Framework Moniker
if: matrix.dotnet-version == '9.0.x'
run: echo "DOTNET_FX_VERSION=net9.0" >> $GITHUB_ENV

- name: Install Dependencies
run: dotnet restore
run: dotnet restore -p:TargetFrameworks=${{ env.DOTNET_FX_VERSION }}

- name: Build
run: dotnet build --configuration Release --no-restore --nologo
run: dotnet build --configuration Release --no-restore --nologo -f ${{ env.DOTNET_FX_VERSION }}

- name: Test
run: dotnet test --configuration Release --no-build --no-restore --verbosity normal
run: dotnet test --configuration Release --no-build --no-restore --verbosity normal -f ${{ env.DOTNET_FX_VERSION }}

publish:
name: Publish Package
Expand All @@ -49,7 +65,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
dotnet-version: 9.0.x

- name: Install Dependencies
run: dotnet restore
Expand Down
28 changes: 22 additions & 6 deletions .github/workflows/pr-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,37 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet-version: [6.0.x, 7.0.x, 8.0.x]
dotnet-version: [6.0.x, 7.0.x, 8.0.x,9.0.x]

steps:
- uses: actions/checkout@v4

- name: Setup .NET

- name: Emit .NET 6.0 Framework Moniker
if: matrix.dotnet-version == '6.0.x'
run: echo "DOTNET_FX_VERSION=net6.0" >> $GITHUB_ENV

- name: Emit .NET 7.0 Framework Moniker
if: matrix.dotnet-version == '7.0.x'
run: echo "DOTNET_FX_VERSION=net7.0" >> $GITHUB_ENV

- name: Emit .NET 8.0 Framework Moniker
if: matrix.dotnet-version == '8.0.x'
run: echo "DOTNET_FX_VERSION=net8.0" >> $GITHUB_ENV

- name: Emit .NET 9.0 Framework Moniker
if: matrix.dotnet-version == '9.0.x'
run: echo "DOTNET_FX_VERSION=net9.0" >> $GITHUB_ENV

- name: Setup .NET ${{ matrix.dotnet-version }}
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet-version }}

- name: Install dependencies
run: dotnet restore
run: dotnet restore -p:TargetFrameworks=${{ env.DOTNET_FX_VERSION }}

- name: Build
run: dotnet build -c Release --no-restore
run: dotnet build -c Release --no-restore -f ${{ env.DOTNET_FX_VERSION }}

- name: Test
run: dotnet test -c Release --no-build --no-restore
run: dotnet test -c Release --no-build --no-restore -f ${{ env.DOTNET_FX_VERSION }}
29 changes: 23 additions & 6 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,24 +19,41 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
dotnet-version: [6.0.x, 7.0.x, 8.0.x]
dotnet-version: [6.0.x, 7.0.x, 8.0.x,9.0.x]

steps:
- uses: actions/checkout@v4

- name: Setup .NET
- name: Emit .NET 6.0 Framework Moniker
if: matrix.dotnet-version == '6.0.x'
run: echo "DOTNET_FX_VERSION=net6.0" >> $GITHUB_ENV

- name: Emit .NET 7.0 Framework Moniker
if: matrix.dotnet-version == '7.0.x'
run: echo "DOTNET_FX_VERSION=net7.0" >> $GITHUB_ENV

- name: Emit .NET 8.0 Framework Moniker
if: matrix.dotnet-version == '8.0.x'
run: echo "DOTNET_FX_VERSION=net8.0" >> $GITHUB_ENV

- name: Emit .NET 9.0 Framework Moniker
if: matrix.dotnet-version == '9.0.x'
run: echo "DOTNET_FX_VERSION=net9.0" >> $GITHUB_ENV


- name: Setup .NET ${{ matrix.dotnet-version }}
uses: actions/setup-dotnet@v4
with:
dotnet-version: ${{ matrix.dotnet-version }}

- name: Install dependencies
run: dotnet restore
run: dotnet restore -p:TargetFrameworks=${{ env.DOTNET_FX_VERSION }}

- name: Build
run: dotnet build -c Release --no-restore
run: dotnet build -c Release --no-restore -f ${{ env.DOTNET_FX_VERSION }}

- name: Test
run: dotnet test -c Release --no-build --no-restore
run: dotnet test -c Release --no-build --no-restore -f ${{ env.DOTNET_FX_VERSION }}

publish:
runs-on: ubuntu-latest
Expand All @@ -48,7 +65,7 @@ jobs:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: 8.0.x
dotnet-version: 9.0.x

- name: Extract the Version
run: echo "VERSION=$(echo ${{ github.event.release.tag_name }} | sed -e 's/^v//')" >> $GITHUB_ENV
Expand Down
4 changes: 2 additions & 2 deletions src/Deveel.Results/Deveel.Results.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<RootNamespace>Deveel</RootNamespace>
Expand All @@ -13,7 +13,7 @@
<PropertyGroup>
<Authors>Antonello Provenzano</Authors>
<Company>Deveel</Company>
<Copyright>2024 (C) Antonello Provenzano</Copyright>
<Copyright>2024-2025 (C) Antonello Provenzano</Copyright>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
<Title>Deveel Results</Title>
<Description>A simple and unambitious library to implement the result pattern in services.</Description>
Expand Down
38 changes: 27 additions & 11 deletions src/Deveel.Results/OperationResultExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,33 @@ public static class OperationResultExtensions
public static bool IsSuccess(this IOperationResult result)
=> result.ResultType == OperationResultType.Success;

/// <summary>
/// Determines if the operation result is an error.
/// </summary>
/// <param name="result">
/// The operation result to check.
/// </param>
/// <returns>
/// Returns <see langword="true"/> if the operation result is an error,
/// otherwise <see langword="false"/>.
/// </returns>
public static bool IsError(this IOperationResult result)
/// <summary>
/// Determines if the operation result is a success and has a value.
/// </summary>
/// <typeparam name="T">
/// The type of the value that is expected to be returned by the operation.
/// </typeparam>
/// <param name="result">
/// The operation result to check.
/// </param>
/// <returns>
/// Returns <see langword="true"/> if the operation result is a success
/// and the value is not <see langword="null"/>, otherwise <see langword="false"/>.
/// </returns>
public static bool HasValue<T>(this IOperationResult<T> result)
=> result.IsSuccess() && result.Value is not null;

/// <summary>
/// Determines if the operation result is an error.
/// </summary>
/// <param name="result">
/// The operation result to check.
/// </param>
/// <returns>
/// Returns <see langword="true"/> if the operation result is an error,
/// otherwise <see langword="false"/>.
/// </returns>
public static bool IsError(this IOperationResult result)
=> result.ResultType == OperationResultType.Error;

/// <summary>
Expand Down
44 changes: 44 additions & 0 deletions src/Deveel.Results/ValidationErrorExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
namespace Deveel
{
/// <summary>
/// Extensions for the <see cref="IValidationError"/> contract.
/// </summary>
public static class ValidationErrorExtensions
{
/// <summary>
/// Gets a dictionary of member names and the list of error messages
/// </summary>
/// <param name="error">
/// The validation error to get the member errors from.
/// </param>
/// <returns>
/// Returns a dictionary where the key is the member name and the value
/// is the list of error messages for that member.
/// </returns>
public static IDictionary<string, string[]> GetMemberErrors(this IValidationError error)
{
ArgumentNullException.ThrowIfNull(error, nameof(error));

var results = new Dictionary<string, List<string>>();

foreach (var result in error.ValidationResults)
{
foreach (var memberName in result.MemberNames)
{
if (String.IsNullOrWhiteSpace(result.ErrorMessage))
continue;

if (!results.TryGetValue(memberName, out var messages))
{
messages = new List<string>();
results[memberName] = messages;
}

messages.Add(result.ErrorMessage);
}
}

return results.ToDictionary(x => x.Key, x => x.Value.ToArray());
}
}
}
16 changes: 11 additions & 5 deletions test/Deveel.Results.XUnit/Deveel.Results.XUnit.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net6.0;net7.0;net8.0</TargetFrameworks>
<TargetFrameworks>net6.0;net7.0;net8.0;net9.0</TargetFrameworks>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

Expand All @@ -11,10 +11,16 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="xunit" Version="2.5.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.3" />
<PackageReference Include="coverlet.collector" Version="6.0.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
Expand Down
71 changes: 70 additions & 1 deletion test/Deveel.Results.XUnit/OperationErrorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,5 +133,74 @@ public static void OperationException_InnerExceptionIsOperationError()
Assert.Equal("err.2", innerError.Code);
Assert.Equal("biz", innerError.Domain);
}
}

[Fact]
public static void ValidationError_WithNullCode()
{
Assert.Throws<ArgumentNullException>(() => new OperationValidationError(null, "biz", Array.Empty<ValidationResult>()));

Check warning on line 140 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / Build and Test (6.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 140 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / Build and Test (9.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 140 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / Build and Test (7.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 140 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / Build and Test (8.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 140 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / build (7.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 140 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / build (8.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 140 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / build (9.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 140 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / build (6.0.x)

Cannot convert null literal to non-nullable reference type.
}

[Fact]
public static void ValidationError_WithNullDomain()
{
Assert.Throws<ArgumentNullException>(() => new OperationValidationError("err.1", null, Array.Empty<ValidationResult>()));

Check warning on line 146 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / Build and Test (6.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 146 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / Build and Test (9.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 146 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / Build and Test (7.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 146 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / Build and Test (8.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 146 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / build (7.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 146 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / build (8.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 146 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / build (9.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 146 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / build (6.0.x)

Cannot convert null literal to non-nullable reference type.
}

[Fact]
public static void ValidationError_WithNullResults()
{
Assert.Throws<ArgumentNullException>(() => new OperationValidationError("err.1", "biz", null));

Check warning on line 152 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / Build and Test (6.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 152 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / Build and Test (9.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 152 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / Build and Test (7.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 152 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / Build and Test (8.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 152 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / build (7.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 152 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / build (8.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 152 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / build (9.0.x)

Cannot convert null literal to non-nullable reference type.

Check warning on line 152 in test/Deveel.Results.XUnit/OperationErrorTests.cs

View workflow job for this annotation

GitHub Actions / build (6.0.x)

Cannot convert null literal to non-nullable reference type.
}

[Fact]
public static void ValidationError_WithResults_GetMemberErrors()
{
var results = new[] {
new ValidationResult("First error of the validation", new []{ "Member1" }),
new ValidationResult("Second error of the validation", new []{"Member2"})
};

var error = new OperationValidationError("err.1", "biz", results);
var memberErrors = error.GetMemberErrors();
Assert.NotNull(memberErrors);
Assert.Equal(2, memberErrors.Count);
Assert.True(memberErrors.TryGetValue("Member1", out var member1Errors));
Assert.NotNull(member1Errors);
Assert.Equal(1, member1Errors.Length);
Assert.Equal("First error of the validation", member1Errors[0]);
Assert.True(memberErrors.TryGetValue("Member2", out var member2Errors));
Assert.NotNull(member2Errors);
Assert.Equal(1, member2Errors.Length);
Assert.Equal("Second error of the validation", member2Errors[0]);
}

[Fact]
public static void ValidationError_WithResults_GetMemberErrors_Empty()
{
var error = new OperationValidationError("err.1", "biz", Array.Empty<ValidationResult>());
var memberErrors = error.GetMemberErrors();
Assert.NotNull(memberErrors);
Assert.Empty(memberErrors);
}

[Fact]
public static void ValidationError_WithResultsForSameMember()
{
var results = new[] {
new ValidationResult("First error of the validation", new []{ "Member" }),
new ValidationResult("Second error of the validation", new []{"Member"})
};

var error = new OperationValidationError("err.1", "biz", results);
var memberErrors = error.GetMemberErrors();

Assert.NotNull(memberErrors);
Assert.Single(memberErrors);
Assert.True(memberErrors.TryGetValue("Member", out var memberErrorsList));
Assert.NotNull(memberErrorsList);
Assert.Equal(2, memberErrorsList.Length);
Assert.Equal("First error of the validation", memberErrorsList[0]);
Assert.Equal("Second error of the validation", memberErrorsList[1]);
}
}
}

0 comments on commit 17d8748

Please sign in to comment.