Skip to content

Commit

Permalink
Comments + middleware feature + CqrsVibe.AspNetCore package
Browse files Browse the repository at this point in the history
  • Loading branch information
velunin committed Jul 11, 2021
1 parent 6446089 commit 85a3bb9
Show file tree
Hide file tree
Showing 49 changed files with 1,080 additions and 225 deletions.
1 change: 1 addition & 0 deletions .github/workflows/release-preview.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,6 @@ jobs:
dotnet nuget push CqrsVibe.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
dotnet nuget push CqrsVibe.MicrosoftDependencyInjection.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
dotnet nuget push CqrsVibe.FluentValidation.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
dotnet nuget push CqrsVibe.AspNetCore.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
env:
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
1 change: 1 addition & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,6 @@ jobs:
dotnet nuget push CqrsVibe.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
dotnet nuget push CqrsVibe.MicrosoftDependencyInjection.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
dotnet nuget push CqrsVibe.FluentValidation.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
dotnet nuget push CqrsVibe.AspNetCore.${VERSION}.nupkg --source https://api.nuget.org/v3/index.json --api-key ${NUGET_TOKEN}
env:
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
17 changes: 17 additions & 0 deletions src/CqrsVibe.AspNetCore/AppBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using CqrsVibe.MicrosoftDependencyInjection;
using Microsoft.AspNetCore.Builder;

namespace CqrsVibe.AspNetCore
{
public static class AppBuilderExtensions
{
public static IApplicationBuilder UseCqrsVibe(this IApplicationBuilder app)
{
return app.Use((context, next) =>
{
context.RequestServices.SetToHandlerResolverAccessor();
return next();
});
}
}
}
20 changes: 20 additions & 0 deletions src/CqrsVibe.AspNetCore/CqrsVibe.AspNetCore.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<Authors>Vsevolod Elunin</Authors>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/velunin/CqrsVibe</RepositoryUrl>
<PackageTags>CQRS;Mediator;Commands;Queries</PackageTags>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\CqrsVibe.MicrosoftDependencyInjection\CqrsVibe.MicrosoftDependencyInjection.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="1.0.0" />
</ItemGroup>

</Project>
88 changes: 88 additions & 0 deletions src/CqrsVibe.FluentValidation/CommandValidationFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
using System;
using System.Threading.Tasks;
using CqrsVibe.Commands;
using CqrsVibe.Commands.Pipeline;
using CqrsVibe.ContextAbstractions;
using CqrsVibe.FluentValidation.Extensions;
using FluentValidation;
using GreenPipes;

namespace CqrsVibe.FluentValidation
{
/// <summary>
/// Command validation filter
/// </summary>
internal class CommandValidationFilter : IFilter<ICommandHandlingContext>
{
private readonly OnFailureBehavior _behavior;

public CommandValidationFilter(OnFailureBehavior behavior)
{
_behavior = behavior;
}

public async Task Send(ICommandHandlingContext context, IPipe<ICommandHandlingContext> next)
{
var commandValidatorType = typeof(IValidator<>).MakeGenericType(context.Command.GetType());

if(!context.ContextServices.TryResolveValidator(commandValidatorType, out var validator))
{
await next.Send(context);
return;
}

ValidateBehavior(context);

var validationContext = new ValidationContext<ICommand>(context.Command);
var validationResult = await validator.ValidateAsync(
validationContext,
context.CancellationToken);

switch (_behavior)
{
case OnFailureBehavior.ThrowException:
if (!validationResult.IsValid)
{
throw new ValidationException(validationResult.Errors);
}
break;
case OnFailureBehavior.ReturnEither:
if (!validationResult.IsValid)
{
var validationState = ValidationState.ErrorState(validationResult);

context.Command.TryGetResultType(out var resultType);

((IResultingHandlingContext)context).SetResult(Activator.CreateInstance(resultType, validationState));

return;
}
break;
default:
throw new InvalidOperationException("Undefined behavior");
}

await next.Send(context);
}

public void Probe(ProbeContext context)
{
var scope = context.CreateFilterScope("commandValidation");
scope.Add("onFailureBehavior", _behavior.ToString("G"));
}

private void ValidateBehavior(ICommandHandlingContext context)
{
if (_behavior == OnFailureBehavior.ReturnEither)
{
if (!context.Command.TryGetResultType(out var commandResultType) ||
!commandResultType.IsEitherWithValidationResult())
{
throw new InvalidOperationException(
$"For mode with '{OnFailureBehavior.ReturnEither:G}' behavior " +
"command result type must be Either<TResult,ValidationState>");
}
}
}
}
}
5 changes: 5 additions & 0 deletions src/CqrsVibe.FluentValidation/Either.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@

namespace CqrsVibe.FluentValidation
{
/// <summary>
/// Either monad
/// </summary>
/// <typeparam name="TLeft"></typeparam>
/// <typeparam name="TRight"></typeparam>
public readonly struct Either<TLeft, TRight>
{
private readonly TLeft _left;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,23 @@ namespace CqrsVibe.FluentValidation.Extensions
{
public static class ConfiguratorExtensions
{
/// <summary>
/// Configure commands validation
/// </summary>
/// <param name="configurator">Pipeline configurator</param>
/// <param name="behavior">Behavior on failure</param>
public static void UseFluentValidation(
this IPipeConfigurator<ICommandHandlingContext> configurator,
OnFailureBehavior behavior)
{
configurator.AddPipeSpecification(new ValidationSpecification(behavior));
}


/// <summary>
/// Configure queries validation
/// </summary>
/// <param name="configurator">Pipeline configurator</param>
/// <param name="behavior">Behavior on failure</param>
public static void UseFluentValidation(
this IPipeConfigurator<IQueryHandlingContext> configurator,
OnFailureBehavior behavior)
Expand Down
3 changes: 3 additions & 0 deletions src/CqrsVibe.FluentValidation/OnFailureBehavior.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
namespace CqrsVibe.FluentValidation
{
/// <summary>
/// Behavior when validation failed
/// </summary>
public enum OnFailureBehavior
{
ThrowException,
Expand Down
86 changes: 86 additions & 0 deletions src/CqrsVibe.FluentValidation/QueryValidationFilter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
using System;
using System.Threading.Tasks;
using CqrsVibe.FluentValidation.Extensions;
using CqrsVibe.Queries;
using CqrsVibe.Queries.Pipeline;
using FluentValidation;
using GreenPipes;

namespace CqrsVibe.FluentValidation
{
/// <summary>
/// Query validation filter
/// </summary>
internal class QueryValidationFilter : IFilter<IQueryHandlingContext>
{
private readonly OnFailureBehavior _behavior;

public QueryValidationFilter(OnFailureBehavior behavior)
{
_behavior = behavior;
}

public async Task Send(IQueryHandlingContext context, IPipe<IQueryHandlingContext> next)
{
var commandValidatorType = typeof(IValidator<>).MakeGenericType(context.Query.GetType());

if(!context.ContextServices.TryResolveValidator(commandValidatorType, out var validator))
{
await next.Send(context);
return;
}

ValidateBehavior(context);

var validationContext = new ValidationContext<IQuery>(context.Query);
var validationResult = await validator.ValidateAsync(
validationContext,
context.CancellationToken);

switch (_behavior)
{
case OnFailureBehavior.ThrowException:
if (!validationResult.IsValid)
{
throw new ValidationException(validationResult.Errors);
}
break;
case OnFailureBehavior.ReturnEither:
if (!validationResult.IsValid)
{
var validationState = ValidationState.ErrorState(validationResult);

context.Query.TryGetResultType(out var resultType);

context.SetResult(Activator.CreateInstance(resultType, validationState));
return;
}
break;
default:
throw new InvalidOperationException("Undefined behavior");
}

await next.Send(context);
}

public void Probe(ProbeContext context)
{
var scope = context.CreateFilterScope("queryValidation");
scope.Add("onFailureBehavior", _behavior.ToString("G"));
}

private void ValidateBehavior(IQueryHandlingContext context)
{
if (_behavior == OnFailureBehavior.ReturnEither)
{
if (!context.Query.TryGetResultType(out var commandResultType) ||
!commandResultType.IsEitherWithValidationResult())
{
throw new InvalidOperationException(
$"For mode with '{OnFailureBehavior.ReturnEither:G}' behavior " +
"query result type must be Either<TResult,ValidationState>");
}
}
}
}
}
52 changes: 0 additions & 52 deletions src/CqrsVibe.FluentValidation/ValidationResultTaskFactory.cs

This file was deleted.

Loading

0 comments on commit 85a3bb9

Please sign in to comment.