From b347adc39a52dbfe3e60eb52b5f65a6fe6c71c72 Mon Sep 17 00:00:00 2001 From: Sebastian Kleinschmager Date: Tue, 5 Apr 2022 16:40:37 +0200 Subject: [PATCH] Update MediatR to 10, some chore work --- ...atR.Extensions.Microsoft.AspNetCore.csproj | 4 +- ...ortedCancellationTokenMediatorDecorator.cs | 50 +++++++- ...tensions.Microsoft.AspNetCore.Tests.csproj | 15 +-- ...dCancellationTokenMediatorDecoratorTest.cs | 118 ++++++++++++++++-- .../ServiceRegistrationTests.cs | 22 +++- 5 files changed, 183 insertions(+), 26 deletions(-) diff --git a/src/MediatR.Extensions.Microsoft.AspNetCore/MediatR.Extensions.Microsoft.AspNetCore.csproj b/src/MediatR.Extensions.Microsoft.AspNetCore/MediatR.Extensions.Microsoft.AspNetCore.csproj index 5defe32..c5a5e21 100644 --- a/src/MediatR.Extensions.Microsoft.AspNetCore/MediatR.Extensions.Microsoft.AspNetCore.csproj +++ b/src/MediatR.Extensions.Microsoft.AspNetCore/MediatR.Extensions.Microsoft.AspNetCore.csproj @@ -17,7 +17,7 @@ Copyright Sebastian Kleinschmager LICENSE - 4.0.0 + 5.0.0 true true @@ -37,7 +37,7 @@ - + diff --git a/src/MediatR.Extensions.Microsoft.AspNetCore/Mediator/RequestAbortedCancellationTokenMediatorDecorator.cs b/src/MediatR.Extensions.Microsoft.AspNetCore/Mediator/RequestAbortedCancellationTokenMediatorDecorator.cs index c130422..2a408a3 100644 --- a/src/MediatR.Extensions.Microsoft.AspNetCore/Mediator/RequestAbortedCancellationTokenMediatorDecorator.cs +++ b/src/MediatR.Extensions.Microsoft.AspNetCore/Mediator/RequestAbortedCancellationTokenMediatorDecorator.cs @@ -1,4 +1,4 @@ - +using System.Runtime.CompilerServices; using Microsoft.AspNetCore.Http; namespace MediatR.Extensions.Microsoft.AspNetCore.Mediator; @@ -36,11 +36,26 @@ public async Task Send(IRequest request, Cancel public async Task Send(object request, CancellationToken cancellationToken = default) { return await PossiblyWrapSendCallWithLinkedCancellationToken( - async token => await _mediator.Send(request, token).ConfigureAwait(false), - cancellationToken) + async token => await _mediator.Send(request, token).ConfigureAwait(false), + cancellationToken) .ConfigureAwait(false); } + public IAsyncEnumerable CreateStream(IStreamRequest request, + CancellationToken cancellationToken = default) + { + return PossiblyWrapCreateStreamCallWithNewCancellationToken( + token => _mediator.CreateStream(request, token), + cancellationToken); + } + + public IAsyncEnumerable CreateStream(object request, CancellationToken cancellationToken = default) + { + return PossiblyWrapCreateStreamCallWithNewCancellationToken( + token => _mediator.CreateStream(request, token), + cancellationToken); + } + public async Task Publish(object notification, CancellationToken cancellationToken = default) { await PossiblyWrapPublishCallWithNewCancellationToken( @@ -73,6 +88,35 @@ private async Task PossiblyWrapSendCallWithLinkedCancellationToken PossiblyWrapCreateStreamCallWithNewCancellationToken( + Func> wrappedCreateStream, + [EnumeratorCancellation] CancellationToken cancellationToken) + { + if (cancellationToken != default && _httpContextAccessor.HttpContext != null) + { + using var cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, _httpContextAccessor.HttpContext.RequestAborted); + var token = cancellationTokenSource.Token; + await foreach (var value in wrappedCreateStream(token).ConfigureAwait(false)) + { + yield return value; + } + } + else if (cancellationToken == default && _httpContextAccessor.HttpContext != null) + { + await foreach (var value in wrappedCreateStream(_httpContextAccessor.HttpContext.RequestAborted).ConfigureAwait(false)) + { + yield return value; + } + } + else + { + await foreach (var value in wrappedCreateStream(cancellationToken).ConfigureAwait(false)) + { + yield return value; + } + } + } + private async Task PossiblyWrapPublishCallWithNewCancellationToken(Func wrappedPublish, CancellationToken cancellationToken) { if (cancellationToken != default && _httpContextAccessor.HttpContext != null) diff --git a/test/MediatR.Extensions.Microsoft.AspNetCore.Tests/MediatR.Extensions.Microsoft.AspNetCore.Tests.csproj b/test/MediatR.Extensions.Microsoft.AspNetCore.Tests/MediatR.Extensions.Microsoft.AspNetCore.Tests.csproj index 6075ef0..4c87875 100644 --- a/test/MediatR.Extensions.Microsoft.AspNetCore.Tests/MediatR.Extensions.Microsoft.AspNetCore.Tests.csproj +++ b/test/MediatR.Extensions.Microsoft.AspNetCore.Tests/MediatR.Extensions.Microsoft.AspNetCore.Tests.csproj @@ -1,21 +1,22 @@ - + net6.0 enable + enable false - - - - - + + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + diff --git a/test/MediatR.Extensions.Microsoft.AspNetCore.Tests/Mediator/RequestAbortedCancellationTokenMediatorDecoratorTest.cs b/test/MediatR.Extensions.Microsoft.AspNetCore.Tests/Mediator/RequestAbortedCancellationTokenMediatorDecoratorTest.cs index f03bf23..0676a4d 100644 --- a/test/MediatR.Extensions.Microsoft.AspNetCore.Tests/Mediator/RequestAbortedCancellationTokenMediatorDecoratorTest.cs +++ b/test/MediatR.Extensions.Microsoft.AspNetCore.Tests/Mediator/RequestAbortedCancellationTokenMediatorDecoratorTest.cs @@ -21,11 +21,11 @@ public void SetUp() _subject = new RequestAbortedCancellationTokenMediatorDecorator(_mediator, _httpContextAccessor); } - private IHttpContextAccessor _httpContextAccessor; - private RequestAbortedCancellationTokenMediatorDecorator _subject; - private HttpContext _httpContextStub; - private IMediator _mediator; - private CancellationTokenSource _requestAbortedTokenSource; + private IHttpContextAccessor _httpContextAccessor = null!; + private RequestAbortedCancellationTokenMediatorDecorator _subject = null!; + private HttpContext _httpContextStub = null!; + private IMediator _mediator = null!; + private CancellationTokenSource _requestAbortedTokenSource = null!; [Test] public async Task Strongly_typed_Send_uses_requested_aborted_cancellation_token_if_it_is_available_and_no_explicit_token_was_passed() @@ -64,7 +64,7 @@ public async Task Strongly_typed_Send_falls_back_to_passed_cancellation_token_if { var fakeRequest = Substitute.For>(); var cancellationToken = new CancellationTokenSource().Token; - _httpContextAccessor.HttpContext.Returns((HttpContext)null); + _httpContextAccessor.HttpContext.Returns((HttpContext?)null); await _subject.Send(fakeRequest, cancellationToken); @@ -108,13 +108,113 @@ public async Task Object_typed_Send_falls_back_to_passed_cancellation_token_if_n { var fakeRequest = new object(); var cancellationToken = new CancellationTokenSource().Token; - _httpContextAccessor.HttpContext.Returns((HttpContext)null); + _httpContextAccessor.HttpContext.Returns((HttpContext?)null); await _subject.Send(fakeRequest, cancellationToken); await _mediator.Received(1).Send(fakeRequest, cancellationToken); } + [Test] + public async Task Strongly_typed_CreateStream_uses_requested_aborted_cancellation_token_if_it_is_available_and_no_explicit_token_was_passed() + { + var fakeRequest = Substitute.For>(); + + await foreach (var _ in _subject.CreateStream(fakeRequest)) + { + } + + _ = _mediator.Received(1).CreateStream(fakeRequest, _httpContextStub.RequestAborted); + } + + [Test] + [TestCase("cancelByRequestAbortedToken")] + [TestCase("cancelByPassedToken")] + public async Task Strongly_typed_CreateStream_merges_passed_cancellation_token_with_request_aborted_token_if_both_are_set(string cancellationSource) + { + var fakeRequest = Substitute.For>(); + var cancellationTokenSource = new CancellationTokenSource(); + var cancellationToken = cancellationTokenSource.Token; + + await foreach (var _ in _subject.CreateStream(fakeRequest, cancellationToken)) + { + } + + _ = _mediator.Received(1).CreateStream(fakeRequest, Arg.Do(t => + { + t.IsCancellationRequested.Should().BeFalse(); + if (cancellationSource == "cancelByRequestAbortedToken") + _requestAbortedTokenSource.Cancel(); + else if (cancellationSource == "cancelByPassedToken") + cancellationTokenSource.Cancel(); + t.IsCancellationRequested.Should().BeTrue(); + })); + } + + [Test] + public async Task Strongly_typed_CreateStream_falls_back_to_passed_cancellation_token_if_no_http_context_or_request_aborted_token_is_available() + { + var fakeRequest = Substitute.For>(); + var cancellationToken = new CancellationTokenSource().Token; + _httpContextAccessor.HttpContext.Returns((HttpContext?)null); + + await foreach (var _ in _subject.CreateStream(fakeRequest, cancellationToken)) + { + } + + _ = _mediator.Received(1).CreateStream(fakeRequest, cancellationToken); + } + + [Test] + public async Task Object_typed_CreateStream_uses_requested_aborted_cancellation_token_if_it_is_available_and_no_explicit_token_was_passed() + { + var fakeRequest = new object(); + + await foreach (var _ in _subject.CreateStream(fakeRequest)) + { + } + + _ = _mediator.Received(1).CreateStream(fakeRequest, _httpContextStub.RequestAborted); + } + + [Test] + [TestCase("cancelByRequestAbortedToken")] + [TestCase("cancelByPassedToken")] + public async Task Object_typed_CreateStream_merges_passed_cancellation_token_with_request_aborted_token_if_both_are_set(string cancellationSource) + { + var fakeRequest = new object(); + var cancellationTokenSource = new CancellationTokenSource(); + var cancellationToken = cancellationTokenSource.Token; + + await foreach (var _ in _subject.CreateStream(fakeRequest, cancellationToken)) + { + } + + _ = _mediator.Received(1).CreateStream(fakeRequest, Arg.Do(t => + { + t.IsCancellationRequested.Should().BeFalse(); + if (cancellationSource == "cancelByRequestAbortedToken") + _requestAbortedTokenSource.Cancel(); + else if (cancellationSource == "cancelByPassedToken") + cancellationTokenSource.Cancel(); + t.IsCancellationRequested.Should().BeTrue(); + })); + } + + [Test] + public async Task Object_typed_CreateStream_falls_back_to_passed_cancellation_token_if_no_http_context_or_request_aborted_token_is_available() + { + var fakeRequest = new object(); + var cancellationToken = new CancellationTokenSource().Token; + _httpContextAccessor.HttpContext.Returns((HttpContext?)null); + + await foreach (var _ in _subject.CreateStream(fakeRequest, cancellationToken)) + { + } + + _ = _mediator.Received(1).CreateStream(fakeRequest, cancellationToken); + } + [Test] public async Task Strongly_typed_Publish_uses_requested_aborted_cancellation_token_if_available() { @@ -151,7 +251,7 @@ await _mediator.Received(1).Publish(fakeNotification, Arg.Do( public async Task Strongly_typed_Publish_falls_back_to_passed_cancellation_token_if_no_http_context_or_request_aborted_token_is_available() { var fakeNotification = Substitute.For(); - _httpContextAccessor.HttpContext.Returns((HttpContext)null); + _httpContextAccessor.HttpContext.Returns((HttpContext?)null); var cancellationToken = new CancellationTokenSource().Token; await _subject.Publish(fakeNotification, cancellationToken); @@ -195,7 +295,7 @@ await _mediator.Received(1).Publish(fakeNotification, Arg.Do( public async Task Object_typed_Publish_falls_back_to_passed_cancellation_token_if_no_http_context_or_request_aborted_token_is_available() { var fakeNotification = new object(); - _httpContextAccessor.HttpContext.Returns((HttpContext)null); + _httpContextAccessor.HttpContext.Returns((HttpContext?)null); var cancellationToken = new CancellationTokenSource().Token; await _subject.Publish(fakeNotification, cancellationToken); diff --git a/test/MediatR.Extensions.Microsoft.AspNetCore.Tests/ServiceRegistrationTests.cs b/test/MediatR.Extensions.Microsoft.AspNetCore.Tests/ServiceRegistrationTests.cs index fb52fad..5f48c72 100644 --- a/test/MediatR.Extensions.Microsoft.AspNetCore.Tests/ServiceRegistrationTests.cs +++ b/test/MediatR.Extensions.Microsoft.AspNetCore.Tests/ServiceRegistrationTests.cs @@ -6,6 +6,7 @@ using NUnit.Framework; namespace MediatR.Extensions.Microsoft.AspNetCore.Tests; + [TestFixture] public class ServiceRegistrationTests { @@ -71,7 +72,7 @@ public void Test_type_handler_service_registration_extension_method_with_configu [ExcludeFromCodeCoverage] public class MyFakeMediator : IMediator { - public object ReceivedRequest { get; private set; } + public object? ReceivedRequest { get; private set; } public Task Send(IRequest request, CancellationToken cancellationToken = default) { @@ -79,19 +80,30 @@ public Task Send(IRequest request, Cancellation return Task.FromResult((TResponse)new object()); } - public Task Send(object request, CancellationToken cancellationToken = default) + public Task Send(object request, CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public IAsyncEnumerable CreateStream(IStreamRequest request, + CancellationToken cancellationToken = default) + { + throw new NotImplementedException(); + } + + public IAsyncEnumerable CreateStream(object request, CancellationToken cancellationToken = default) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } public Task Publish(object notification, CancellationToken cancellationToken = default) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } public Task Publish(TNotification notification, CancellationToken cancellationToken = default) where TNotification : INotification { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } }