Skip to content

Commit 844ce81

Browse files
Implement SnapshotIt.FluentValidation layer with comprehensive validation support
Co-authored-by: AkhmedovEhson <129420305+AkhmedovEhson@users.noreply.github.com>
1 parent 1869d15 commit 844ce81

File tree

8 files changed

+537
-3
lines changed

8 files changed

+537
-3
lines changed

SnapshotLibrary.sln

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SnapshotIt.DependencyInject
1515
EndProject
1616
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SnapshotIt.DependencyInjection", "src\SnapshotIt.DependencyInjection\SnapshotIt.DependencyInjection.csproj", "{1FACD53B-C538-41D8-8678-E0D86C4BCFB2}"
1717
EndProject
18-
18+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SnapshotIt.FluentValidation", "src\SnapshotIt.FluentValidation\SnapshotIt.FluentValidation.csproj", "{B765BCBD-7560-4D78-A834-74D32470B84A}"
19+
EndProject
20+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SnapshotIt.FluentValidation.UnitTests", "tests\SnapshotIt.FluentValidation.UnitTests\SnapshotIt.FluentValidation.UnitTests.csproj", "{326CEAAE-4D8C-440F-A458-F7882EE66666}"
21+
EndProject
1922
Global
2023
GlobalSection(SolutionConfigurationPlatforms) = preSolution
2124
Debug|Any CPU = Debug|Any CPU
@@ -38,7 +41,14 @@ Global
3841
{1FACD53B-C538-41D8-8678-E0D86C4BCFB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
3942
{1FACD53B-C538-41D8-8678-E0D86C4BCFB2}.Release|Any CPU.ActiveCfg = Release|Any CPU
4043
{1FACD53B-C538-41D8-8678-E0D86C4BCFB2}.Release|Any CPU.Build.0 = Release|Any CPU
41-
44+
{B765BCBD-7560-4D78-A834-74D32470B84A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
45+
{B765BCBD-7560-4D78-A834-74D32470B84A}.Debug|Any CPU.Build.0 = Debug|Any CPU
46+
{B765BCBD-7560-4D78-A834-74D32470B84A}.Release|Any CPU.ActiveCfg = Release|Any CPU
47+
{B765BCBD-7560-4D78-A834-74D32470B84A}.Release|Any CPU.Build.0 = Release|Any CPU
48+
{326CEAAE-4D8C-440F-A458-F7882EE66666}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
49+
{326CEAAE-4D8C-440F-A458-F7882EE66666}.Debug|Any CPU.Build.0 = Debug|Any CPU
50+
{326CEAAE-4D8C-440F-A458-F7882EE66666}.Release|Any CPU.ActiveCfg = Release|Any CPU
51+
{326CEAAE-4D8C-440F-A458-F7882EE66666}.Release|Any CPU.Build.0 = Release|Any CPU
4252
EndGlobalSection
4353
GlobalSection(SolutionProperties) = preSolution
4454
HideSolutionNode = FALSE
@@ -48,7 +58,8 @@ Global
4858
{6BD3DBA2-2F9A-4013-AD65-72CE36035D59} = {B0FF198B-9C6F-4B77-87DE-AF5D81D0D486}
4959
{7F9EC541-B30F-4601-9649-508E4A0C382E} = {B0FF198B-9C6F-4B77-87DE-AF5D81D0D486}
5060
{1FACD53B-C538-41D8-8678-E0D86C4BCFB2} = {9891F523-3F7C-4EB3-BF06-38078AF2D05A}
51-
61+
{B765BCBD-7560-4D78-A834-74D32470B84A} = {9891F523-3F7C-4EB3-BF06-38078AF2D05A}
62+
{326CEAAE-4D8C-440F-A458-F7882EE66666} = {B0FF198B-9C6F-4B77-87DE-AF5D81D0D486}
5263
EndGlobalSection
5364
GlobalSection(ExtensibilityGlobals) = postSolution
5465
SolutionGuid = {5C844C31-15C2-47F2-B289-6867A7C9C29A}
Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
using FluentValidation;
2+
using FluentValidation.Results;
3+
using System.Diagnostics.CodeAnalysis;
4+
5+
namespace SnapshotIt.FluentValidation
6+
{
7+
/// <summary>
8+
/// FluentValidation extensions for SnapshotIt capturing functionality
9+
/// </summary>
10+
public static class CaptureValidationExtensions
11+
{
12+
/// <summary>
13+
/// Posts an object to the snapshot collection with validation using FluentValidation
14+
/// </summary>
15+
/// <typeparam name="T">The type of object to validate and post</typeparam>
16+
/// <param name="snapshot">The snapshot instance</param>
17+
/// <param name="input">The object to validate and post</param>
18+
/// <param name="validator">The FluentValidation validator to use</param>
19+
/// <exception cref="ValidationException">Thrown when validation fails</exception>
20+
public static void PostWithValidation<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(
21+
this ISnapshot snapshot,
22+
T input,
23+
IValidator<T> validator)
24+
{
25+
var validationResult = validator.Validate(input);
26+
27+
if (!validationResult.IsValid)
28+
{
29+
throw new ValidationException(validationResult.Errors);
30+
}
31+
32+
snapshot.Post(input);
33+
}
34+
35+
/// <summary>
36+
/// Posts an object to the snapshot collection with validation using FluentValidation asynchronously
37+
/// </summary>
38+
/// <typeparam name="T">The type of object to validate and post</typeparam>
39+
/// <param name="snapshot">The snapshot instance</param>
40+
/// <param name="input">The object to validate and post</param>
41+
/// <param name="validator">The FluentValidation validator to use</param>
42+
/// <exception cref="ValidationException">Thrown when validation fails</exception>
43+
public static async Task PostWithValidationAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(
44+
this ISnapshot snapshot,
45+
T input,
46+
IValidator<T> validator)
47+
{
48+
var validationResult = await validator.ValidateAsync(input);
49+
50+
if (!validationResult.IsValid)
51+
{
52+
throw new ValidationException(validationResult.Errors);
53+
}
54+
55+
await snapshot.PostAsync(input);
56+
}
57+
58+
/// <summary>
59+
/// Posts multiple objects to the snapshot collection with validation using FluentValidation asynchronously
60+
/// </summary>
61+
/// <typeparam name="T">The type of objects to validate and post</typeparam>
62+
/// <param name="snapshot">The snapshot instance</param>
63+
/// <param name="values">The objects to validate and post</param>
64+
/// <param name="validator">The FluentValidation validator to use</param>
65+
/// <exception cref="ValidationException">Thrown when validation fails for any object</exception>
66+
public static async Task PostWithValidationAsync<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] T>(
67+
this ISnapshot snapshot,
68+
T[] values,
69+
IValidator<T> validator)
70+
{
71+
var validationTasks = values.Select(value => validator.ValidateAsync(value));
72+
var validationResults = await Task.WhenAll(validationTasks);
73+
74+
var allErrors = validationResults
75+
.Where(result => !result.IsValid)
76+
.SelectMany(result => result.Errors)
77+
.ToList();
78+
79+
if (allErrors.Count > 0)
80+
{
81+
throw new ValidationException(allErrors);
82+
}
83+
84+
await snapshot.PostAsync(values);
85+
}
86+
87+
/// <summary>
88+
/// Validates an object using FluentValidation without posting to snapshot
89+
/// </summary>
90+
/// <typeparam name="T">The type of object to validate</typeparam>
91+
/// <param name="snapshot">The snapshot instance</param>
92+
/// <param name="input">The object to validate</param>
93+
/// <param name="validator">The FluentValidation validator to use</param>
94+
/// <returns>ValidationResult containing validation outcome and errors</returns>
95+
public static ValidationResult Validate<T>(
96+
this ISnapshot snapshot,
97+
T input,
98+
IValidator<T> validator)
99+
{
100+
return validator.Validate(input);
101+
}
102+
103+
/// <summary>
104+
/// Validates an object using FluentValidation without posting to snapshot asynchronously
105+
/// </summary>
106+
/// <typeparam name="T">The type of object to validate</typeparam>
107+
/// <param name="snapshot">The snapshot instance</param>
108+
/// <param name="input">The object to validate</param>
109+
/// <param name="validator">The FluentValidation validator to use</param>
110+
/// <returns>ValidationResult containing validation outcome and errors</returns>
111+
public static Task<ValidationResult> ValidateAsync<T>(
112+
this ISnapshot snapshot,
113+
T input,
114+
IValidator<T> validator)
115+
{
116+
return validator.ValidateAsync(input);
117+
}
118+
}
119+
}
Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
# SnapshotIt.FluentValidation
2+
3+
FluentValidation integration for SnapshotIt library, providing validation capabilities for capturing objects to snapshots.
4+
5+
## Features
6+
7+
- Validate objects before posting to snapshots using FluentValidation
8+
- Support for both synchronous and asynchronous validation
9+
- Validation of single objects and arrays
10+
- Comprehensive error handling with ValidationException
11+
12+
## Usage
13+
14+
### Basic Validation
15+
16+
```csharp
17+
using SnapshotIt.FluentValidation;
18+
using FluentValidation;
19+
20+
// Create a validator
21+
public class ProductValidator : AbstractValidator<Product>
22+
{
23+
public ProductValidator()
24+
{
25+
RuleFor(x => x.Id).GreaterThan(0);
26+
RuleFor(x => x.Name).NotEmpty().MaximumLength(100);
27+
RuleFor(x => x.Price).GreaterThan(0);
28+
}
29+
}
30+
31+
// Use with SnapshotIt
32+
var validator = new ProductValidator();
33+
var product = new Product { Id = 1, Name = "Test Product", Price = 99.99m };
34+
35+
// Post with validation
36+
Snapshot.Out.PostWithValidation(product, validator);
37+
38+
// Or async
39+
await Snapshot.Out.PostWithValidationAsync(product, validator);
40+
```
41+
42+
### Validation Without Posting
43+
44+
```csharp
45+
// Just validate without posting
46+
var result = Snapshot.Out.Validate(product, validator);
47+
if (!result.IsValid)
48+
{
49+
foreach (var error in result.Errors)
50+
{
51+
Console.WriteLine($"{error.PropertyName}: {error.ErrorMessage}");
52+
}
53+
}
54+
55+
// Or async
56+
var result = await Snapshot.Out.ValidateAsync(product, validator);
57+
```
58+
59+
### Array Validation
60+
61+
```csharp
62+
var products = new[] { product1, product2, product3 };
63+
await Snapshot.Out.PostWithValidationAsync(products, validator);
64+
```
65+
66+
## Exception Handling
67+
68+
When validation fails, a `ValidationException` is thrown containing all validation errors:
69+
70+
```csharp
71+
try
72+
{
73+
Snapshot.Out.PostWithValidation(invalidProduct, validator);
74+
}
75+
catch (ValidationException ex)
76+
{
77+
foreach (var error in ex.Errors)
78+
{
79+
Console.WriteLine($"{error.PropertyName}: {error.ErrorMessage}");
80+
}
81+
}
82+
```
83+
84+
## Requirements
85+
86+
- .NET 8.0 or higher
87+
- FluentValidation 11.9.0 or higher
88+
- SnapshotIt library
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net8.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<GeneratePackageOnBuild>True</GeneratePackageOnBuild>
8+
<Title>SnapshotIt.FluentValidation</Title>
9+
<Version>1.0.0</Version>
10+
<Authors>Ehson Akhmedov</Authors>
11+
<Description>FluentValidation integration for SnapshotIt library</Description>
12+
</PropertyGroup>
13+
14+
<ItemGroup>
15+
<PackageReference Include="FluentValidation" Version="11.9.0" />
16+
</ItemGroup>
17+
18+
<ItemGroup>
19+
<ProjectReference Include="..\SnapshotIt\SnapshotIt.csproj" />
20+
</ItemGroup>
21+
22+
</Project>

0 commit comments

Comments
 (0)