Skip to content

Commit

Permalink
Add support for IAsyncEnumerable #175 (#176)
Browse files Browse the repository at this point in the history
* add iasyncenumerable support for netstandard2.1

* add missing withoutresult implementation

* remove compile condition

* add tests and sample endpoint
  • Loading branch information
cmxl authored Oct 25, 2022
1 parent 7b66782 commit bd1b256
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 2 deletions.
40 changes: 40 additions & 0 deletions sample/Sample.FunctionalTests/AuthorEndpoints/StreamEndpoint.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
using Ardalis.HttpClientTestExtensions;
using Sample.FunctionalTests.Models;
using SampleEndpointApp;
using SampleEndpointApp.DataAccess;
using SampleEndpointApp.DomainModel;
using Xunit;

namespace Sample.FunctionalTests.AuthorEndpoints;

public class StreamEndpoint : IClassFixture<CustomWebApplicationFactory<Startup>>
{
private readonly HttpClient _client;

public StreamEndpoint(CustomWebApplicationFactory<Startup> factory)
{
_client = factory.CreateClient();
}

[Fact]
public async Task ReturnsTwoGivenTwoAuthors()
{
var result = await _client.GetAndDeserialize<IEnumerable<Author>>(Routes.Authors.Stream());

Assert.NotNull(result);
Assert.Equal(SeedData.Authors().Count, result.Count());
}

[Fact]
public async Task GivenLongRunningListRequest_WhenTokenSourceCallsForCancellation_RequestIsTerminated()
{
// Arrange, generate a token source that times out instantly
var tokenSource = new CancellationTokenSource(TimeSpan.Zero);

// Act
var request = _client.GetAsync(Routes.Authors.Stream(), tokenSource.Token);

// Assert
var response = await Assert.ThrowsAsync<OperationCanceledException>(async () => await request);
}
}
2 changes: 2 additions & 0 deletions sample/Sample.FunctionalTests/Models/Routes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ public static class Authors

public static string List(int perPage, int page) => $"{BaseRoute}?perPage={perPage}&page={page}";

public static string Stream() => $"{BaseRoute}/stream";

public static string Get(int id) => $"{BaseRoute}/{id}";

public static string Delete(int id) => $"{BaseRoute}/{id}";
Expand Down
3 changes: 2 additions & 1 deletion sample/Sample.FunctionalTests/Sample.FunctionalTests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\SampleEndpointApp\SampleEndpointApp.csproj" />
<ProjectReference Include="..\..\src\Ardalis.ApiEndpoints\Ardalis.ApiEndpoints.csproj"></ProjectReference>
<ProjectReference Include="..\SampleEndpointApp\SampleEndpointApp.csproj" />
</ItemGroup>

</Project>
37 changes: 37 additions & 0 deletions sample/SampleEndpointApp/Endpoints/Authors/Stream.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System.Runtime.CompilerServices;
using Ardalis.ApiEndpoints;
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using SampleEndpointApp.DomainModel;

namespace SampleEndpointApp.Endpoints.Authors;

public class Stream : EndpointBaseAsync
.WithoutRequest
.WithAsyncEnumerableResult<AuthorListResult>
{
private readonly IAsyncRepository<Author> repository;
private readonly IMapper mapper;

public Stream(
IAsyncRepository<Author> repository,
IMapper mapper)
{
this.repository = repository;
this.mapper = mapper;
}

/// <summary>
/// Stream all authors with a one second delay between entries
/// </summary>
[HttpGet("api/[namespace]/stream")]
public override async IAsyncEnumerable<AuthorListResult> HandleAsync([EnumeratorCancellation] CancellationToken cancellationToken)
{
var result = await repository.ListAllAsync(cancellationToken);
foreach (var author in result)
{
yield return mapper.Map<AuthorListResult>(author);
await Task.Delay(1000, cancellationToken);
}
}
}
2 changes: 1 addition & 1 deletion sample/SampleEndpointApp/SampleEndpointApp.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<ItemGroup>
<ProjectReference Include="..\..\src\Ardalis.ApiEndpoints.CodeAnalyzers\Ardalis.ApiEndpoints.CodeAnalyzers.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<ProjectReference Include="..\..\src\Ardalis.ApiEndpoints.Swashbuckle\Ardalis.ApiEndpoints.Swashbuckle.csproj" />
<ProjectReference Include="..\..\src\Ardalis.ApiEndpoints\Ardalis.ApiEndpoints.csproj" />
<ProjectReference Include="..\..\src\Ardalis.ApiEndpoints\Ardalis.ApiEndpoints.csproj"></ProjectReference>
</ItemGroup>


Expand Down
14 changes: 14 additions & 0 deletions src/Ardalis.ApiEndpoints/FluentGenerics/EndpointBaseAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,13 @@ public abstract Task<ActionResult> HandleAsync(
CancellationToken cancellationToken = default
);
}
public abstract class WithAsyncEnumerableResult<T> : EndpointBase
{
public abstract IAsyncEnumerable<T> HandleAsync(
TRequest request,
CancellationToken cancellationToken = default
);
}
}

public static class WithoutRequest
Expand Down Expand Up @@ -71,5 +78,12 @@ public abstract Task<ActionResult> HandleAsync(
CancellationToken cancellationToken = default
);
}

public abstract class WithAsyncEnumerableResult<T> : EndpointBase
{
public abstract IAsyncEnumerable<T> HandleAsync(
CancellationToken cancellationToken = default
);
}
}
}

0 comments on commit bd1b256

Please sign in to comment.