Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MediatR functional extensions #83

Merged
merged 1 commit into from
Nov 16, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ jobs:
with:
PROJECT_FILE_PATH: src/Codehard.Functional/Codehard.Functional.Marten/Codehard.Functional.Marten.csproj
NUGET_KEY: ${{secrets.NUGET_API_KEY}}
- name: Publish Codehard.Functional.MediatR To Nuget
uses: brandedoutcast/[email protected]
with:
PROJECT_FILE_PATH: src/Codehard.Functional/Codehard.Functional.MediatR/Codehard.Functional.MediatR.csproj
NUGET_KEY: ${{secrets.NUGET_API_KEY}}

publish-common:
runs-on: ubuntu-latest
Expand Down
14 changes: 14 additions & 0 deletions src/Codehard.Core.sln
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codehard.Functional.Marten"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codehard.Functional.Marten.Tests", "Codehard.Functional\Codehard.Functional.Marten.Tests\Codehard.Functional.Marten.Tests.csproj", "{BCB6D4C3-DD27-43AD-B0E9-6C8DE6821AD5}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codehard.Functional.MediatR", "Codehard.Functional\Codehard.Functional.Mediatr\Codehard.Functional.MediatR.csproj", "{8952083F-5A2A-4A37-9039-8588B08CA6EE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Codehard.Functional.MediatR.Tests", "Codehard.Functional\Codehard.Functional.Mediatr.Tests\Codehard.Functional.MediatR.Tests.csproj", "{569F15A1-ABDE-4037-825C-90164D9074E2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -143,6 +147,14 @@ Global
{BCB6D4C3-DD27-43AD-B0E9-6C8DE6821AD5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BCB6D4C3-DD27-43AD-B0E9-6C8DE6821AD5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BCB6D4C3-DD27-43AD-B0E9-6C8DE6821AD5}.Release|Any CPU.Build.0 = Release|Any CPU
{8952083F-5A2A-4A37-9039-8588B08CA6EE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{8952083F-5A2A-4A37-9039-8588B08CA6EE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{8952083F-5A2A-4A37-9039-8588B08CA6EE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{8952083F-5A2A-4A37-9039-8588B08CA6EE}.Release|Any CPU.Build.0 = Release|Any CPU
{569F15A1-ABDE-4037-825C-90164D9074E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{569F15A1-ABDE-4037-825C-90164D9074E2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{569F15A1-ABDE-4037-825C-90164D9074E2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{569F15A1-ABDE-4037-825C-90164D9074E2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -173,5 +185,7 @@ Global
{6277AC9F-217C-4F89-8589-51FCB4E9BADF} = {2F98EF48-527C-44C5-8FC3-65F25C808AC9}
{8D62918D-C27F-45AF-B26A-437B594C0D5C} = {0C257E94-AD98-4AFB-93B7-B6F64EB7D2BA}
{BCB6D4C3-DD27-43AD-B0E9-6C8DE6821AD5} = {0C257E94-AD98-4AFB-93B7-B6F64EB7D2BA}
{8952083F-5A2A-4A37-9039-8588B08CA6EE} = {0C257E94-AD98-4AFB-93B7-B6F64EB7D2BA}
{569F15A1-ABDE-4037-825C-90164D9074E2} = {0C257E94-AD98-4AFB-93B7-B6F64EB7D2BA}
EndGlobalSection
EndGlobal
3 changes: 2 additions & 1 deletion src/Codehard.Core.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/UserDictionary/Words/=Codehard/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Codehard/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Mediat/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="LanguageExt.Core" Version="4.4.3" />
<PackageReference Include="LanguageExt.Core" Version="4.4.7" />
<PackageReference Update="FSharp.Core" Version="7.0.300" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="LanguageExt.Core" Version="4.4.3" />
<PackageReference Include="LanguageExt.Core" Version="4.4.7" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.9" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="7.0.9" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

<ItemGroup>
<PackageReference Include="FSharp.Core" Version="7.0.300" />
<PackageReference Include="LanguageExt.Core" Version="4.4.3" />
<PackageReference Include="LanguageExt.Core" Version="4.4.7" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Version>3.0.0-preview-1</Version>
<Version>3.0.0-preview-2</Version>
<Description>A functional extensions for Marten.</Description>
<PackageProjectUrl>https://github.com/codehardth/Codehard.Functional</PackageProjectUrl>
<RepositoryUrl>https://github.com/codehardth/Codehard.Functional</RepositoryUrl>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,12 @@ public static class DocumentSessionExtensions
/// <summary>
/// Save changes to the database
/// </summary>
public static Aff<Unit> SaveChangesAff(this IDocumentSession documentSession)
public static Aff<Unit> SaveChangesAff(
this IDocumentSession documentSession, CancellationToken cancellationToken = default)
{
return Aff(async () => await documentSession.SaveChangesAsync().ToUnit());
return Aff(
async () =>
await documentSession.SaveChangesAsync(cancellationToken).ToUnit());
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,19 @@ namespace Marten;

public static class QueryableExtensions
{
/// <summary>
/// Asynchronously converts an IQueryable&lt;T&gt; into a read-only list within an Aff monad.
/// </summary>
/// <typeparam name="T">The type of elements in the IQueryable.</typeparam>
/// <param name="source">The IQueryable to be converted to a read-only list.</param>
/// <param name="ct">A CancellationToken to observe while waiting for the task to complete.</param>
/// <returns>An Aff&lt;IReadOnlyList&lt;T&gt;&gt; representing the asynchronous operation.
/// The Aff monad wraps the result, which is the read-only list of elements.</returns>
public static Aff<IReadOnlyList<T>> ToListAff<T>(this IQueryable<T> source, CancellationToken ct = default)
{
return Aff(async () => await source.ToListAsync(ct));
}

/// <summary>
/// Asynchronously returns the only element of a sequence, or a None value if the sequence is empty;
/// this method returns an Option&lt;TSource&gt;.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsPackable>false</IsPackable>

<AssemblyName>Codehard.Functional.MediatR.Tests</AssemblyName>

<RootNamespace>Codehard.Functional.MediatR.Tests</RootNamespace>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Lamar" Version="12.0.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0"/>
<PackageReference Include="Shouldly" Version="4.2.1" />
<PackageReference Include="xunit" Version="2.4.1"/>
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="coverlet.collector" Version="3.1.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\Codehard.Functional\Codehard.Functional.Mediatr\Codehard.Functional.Mediatr.csproj" />
<ProjectReference Include="..\..\Codehard.Functional\Codehard.Functional\Codehard.Functional.csproj" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
using System.Text;
using Lamar;
using MediatR;
using Shouldly;

namespace Codehard.Functional.MediatR.Tests;

public class PublishTests
{
public class Ping : INotification
{
public string Message { get; init; }
}

[Fact]
public async Task WhenPublishMessage_ShouldNotifidEachHandlersCorrectly()
{
// Arrange
var builder = new StringBuilder();
var writer = new StringWriter(builder);
var container = BuildMediatr();

// Act
var mediator = container.GetInstance<IMediator>();

await mediator.PublishAff(new Ping { Message = "Ping" })
.Run();

// Assert
var result = builder.ToString().Split(new[] { Environment.NewLine }, StringSplitOptions.None);
result.ShouldContain("Ping Pong");
result.ShouldContain("Ping Pung");

Container BuildMediatr()
{
var container = new Container(cfg =>
{
cfg.Scan(scanner =>
{
scanner.AssemblyContainingType(typeof(PublishTests));
scanner.IncludeNamespaceContainingType<Ping>();
scanner.WithDefaultConventions();
scanner.AddAllTypesOf(typeof (INotificationHandler<>));
});
cfg.For<TextWriter>().Use(writer);
cfg.For<IMediator>().Use<Mediator>();
});

return container;
}
}

public class PongHandler : INotificationHandler<Ping>
{
private readonly TextWriter _writer;

public PongHandler(TextWriter writer)
{
_writer = writer;
}

public Task Handle(Ping notification, CancellationToken cancellationToken)
{
return _writer.WriteLineAsync(notification.Message + " Pong");
}
}

public class PungHandler : INotificationHandler<Ping>
{
private readonly TextWriter _writer;

public PungHandler(TextWriter writer)
{
_writer = writer;
}

public Task Handle(Ping notification, CancellationToken cancellationToken)
{
return _writer.WriteLineAsync(notification.Message + " Pung");
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
using System.Text;
using Lamar;
using LanguageExt;
using MediatR;
using Shouldly;

namespace Codehard.Functional.MediatR.Tests;

public class QueryTests
{
public class Ping : IQuery<PongQueryResult>
{
public string? Message { get; set; }
}

public class PingNotFound : IQuery<PongQueryResult>
{
public string? Message { get; set; }
}

public class Pong
{
public string? Message { get; set; }
}

public class PingHandler
: IQueryHandler<Ping, PongQueryResult>,
IQueryHandler<PingNotFound, PongQueryResult>
{
public Task<Fin<PongQueryResult>> Handle(Ping request, CancellationToken cancellationToken)
{
return Task.FromResult(
Fin<PongQueryResult>.Succ(
new PongQueryResult.Success(new Pong { Message = request.Message + " Pong" })));
}

public Task<Fin<PongQueryResult>> Handle(PingNotFound request, CancellationToken cancellationToken)
{
return Task.FromResult(
Fin<PongQueryResult>.Fail(
new ExpectedResultError(new PongQueryResult.NotFound())));
}
}

public abstract record PongQueryResult
{
private PongQueryResult()
{
}

public sealed record Success(Pong Pong) : PongQueryResult;

public sealed record NotFound : PongQueryResult;
}

[Fact]
public async Task WhenSendQuery_ShouldResponseCorrectly()
{
// Arrange
var builder = new StringBuilder();
var writer = new StringWriter(builder);
var container = BuildMediatr();

// Act
var mediator = container.GetInstance<IMediator>();

var response =
await mediator
.SendQueryAff<Ping, PongQueryResult>(new Ping { Message = "Ping" })
.MapExpectedResultError()
.Run();

// Assert
Assert.True(response.IsSucc);

var result = response.ThrowIfFail();

var successValue = result.ShouldBeOfType<PongQueryResult.Success>();

successValue.Pong.Message.ShouldBe("Ping Pong");

return;

Container BuildMediatr()
{
var container = new Container(cfg =>
{
cfg.Scan(scanner =>
{
scanner.AssemblyContainingType(typeof(QueryTests));
scanner.IncludeNamespaceContainingType<Ping>();
scanner.WithDefaultConventions();
scanner.AddAllTypesOf(typeof(IRequestHandler<,>));
});
cfg.For<IMediator>().Use<Mediator>();
});

return container;
}
}

[Fact]
public async Task WhenSendNotFoundQuery_ShouldResponseNotFoundCorrectly()
{
// Arrange
var builder = new StringBuilder();
var writer = new StringWriter(builder);
var container = BuildMediatr();

// Act
var mediator = container.GetInstance<IMediator>();

var response =
await mediator
.SendQueryAff<PingNotFound, PongQueryResult>(new PingNotFound { Message = "Ping" })
.MapExpectedResultError()
.Run();

// Assert
Assert.True(response.IsSucc);

var result = response.ThrowIfFail();

result.ShouldBeOfType<PongQueryResult.NotFound>();

return;

Container BuildMediatr()
{
var container = new Container(cfg =>
{
cfg.Scan(scanner =>
{
scanner.AssemblyContainingType(typeof(QueryTests));
scanner.IncludeNamespaceContainingType<Ping>();
scanner.WithDefaultConventions();
scanner.AddAllTypesOf(typeof(IRequestHandler<,>));
});
cfg.For<IMediator>().Use<Mediator>();
});

return container;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
Loading
Loading