Skip to content

Commit

Permalink
Added helpers for commands, queries and event handlers.
Browse files Browse the repository at this point in the history
Strengthen the API tests
  • Loading branch information
oskardudycz committed Jul 20, 2021
1 parent 46fb028 commit eabdead
Show file tree
Hide file tree
Showing 9 changed files with 100 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,8 @@ public async Task InitializeCommand_ShouldCreate_Cart()
var query = $"{createdId}";

//send query
var queryResponse = await fixture.Get(query, 10);
var queryResponse = await fixture.Get(query, 10,
check: response => new(response.StatusCode == HttpStatusCode.Created));
queryResponse.EnsureSuccessStatusCode();

var cartDetails = await queryResponse.GetResultFromJson<CartDetails>();
Expand Down
31 changes: 15 additions & 16 deletions CQRS_Flow/.NET/Carts/Carts.Api/Controllers/CartsController.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Ardalis.GuardClauses;
using Carts.Api.Requests.Carts;
using Carts.Carts.GettingCartAtVersion;
using Carts.Carts.GettingCartById;
using Carts.Carts.GettingCartHistory;
using Carts.Carts.GettingCarts;
using Carts.Carts.Products;
using Microsoft.AspNetCore.Mvc;
using Core.Commands;
Expand Down Expand Up @@ -107,22 +110,18 @@ public Task<CartDetails> Get(Guid id)
return queryBus.Send<GetCartById, CartDetails>(GetCartById.Create(id));
}

// [HttpGet]
// public async Task<PagedListResponse<CartShortInfo>> Get([FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20)
// {
// var pagedList = await queryBus.Send<GetCarts, IPagedList<CartShortInfo>>(GetCarts.Create(pageNumber, pageSize));
//
// return pagedList.ToResponse();
// }
//
//
// [HttpGet("{id}/history")]
// public async Task<PagedListResponse<CartHistory>> GetHistory(Guid id)
// {
// var pagedList = await queryBus.Send<GetCartHistory, IPagedList<CartHistory>>(GetCartHistory.Create(id));
//
// return pagedList.ToResponse();
// }
[HttpGet]
public Task<IReadOnlyList<CartShortInfo>> Get([FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20)
{
return queryBus.Send<GetCarts, IReadOnlyList<CartShortInfo>>(GetCarts.Create(pageNumber, pageSize));
}


[HttpGet("{id}/history")]
public Task<IReadOnlyList<CartHistory>> GetHistory(Guid id)
{
return queryBus.Send<GetCartHistory, IReadOnlyList<CartHistory>>(GetCartHistory.Create(id));
}

[HttpGet("{id}/versions")]
public Task<CartDetails> GetVersion(Guid id, [FromQuery] GetCartAtVersion query)
Expand Down
8 changes: 0 additions & 8 deletions CQRS_Flow/.NET/Carts/Carts.Api/appsettings.Development.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,5 @@
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"EventStore": {
"ConnectionString": "esdb://localhost:2113?tls=false"
},
"ReadModel_Marten": {
"ConnectionString": "PORT = 5432; HOST = localhost; TIMEOUT = 15; POOLING = True; MINPOOLSIZE = 1; MAXPOOLSIZE = 100; COMMANDTIMEOUT = 20; DATABASE = 'postgres'; PASSWORD = 'Password12!'; USER ID = 'postgres'",
"WriteModelSchema": "carts_management_write",
"ReadModelSchema": "carts_management_read"
}
}
18 changes: 10 additions & 8 deletions CQRS_Flow/.NET/Carts/Carts/Carts/Config.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@
using Carts.Carts.InitializingCart;
using Carts.Carts.RemovingProduct;
using Carts.Pricing;
using Core.Commands;
using Core.ElasticSearch.Projections;
using Core.EventStoreDB.Repository;
using Core.Queries;
using Core.Repositories;
using MediatR;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -31,10 +33,10 @@ internal static void AddCarts(this IServiceCollection services)

private static void AddCommandHandlers(IServiceCollection services)
{
services.AddScoped<IRequestHandler<InitializeCart, Unit>, HandleInitializeCart>();
services.AddScoped<IRequestHandler<AddProduct, Unit>, HandleAddProduct>();
services.AddScoped<IRequestHandler<RemoveProduct, Unit>, HandleRemoveProduct>();
services.AddScoped<IRequestHandler<ConfirmCart, Unit>, HandleConfirmCart>();
services.AddCommandHandler<InitializeCart, HandleInitializeCart>()
.AddCommandHandler<AddProduct, HandleAddProduct>()
.AddCommandHandler<RemoveProduct, HandleRemoveProduct>()
.AddCommandHandler<ConfirmCart, HandleConfirmCart>();
}

private static void AddProjections(IServiceCollection services)
Expand All @@ -60,10 +62,10 @@ private static void AddProjections(IServiceCollection services)

private static void AddQueryHandlers(IServiceCollection services)
{
services.AddScoped<IRequestHandler<GetCartById, CartDetails?>, HandleGetCartById>();
services.AddScoped<IRequestHandler<GetCarts, IReadOnlyList<CartShortInfo>>, HandleGetCarts>();
services.AddScoped<IRequestHandler<GetCartHistory, IReadOnlyList<CartHistory>>, HandleGetCartHistory>();
services.AddScoped<IRequestHandler<GetCartAtVersion, CartDetails>, HandleGetCartAtVersion>();
services.AddQueryHandler<GetCartById, CartDetails, HandleGetCartById>()
.AddQueryHandler<GetCarts, IReadOnlyList<CartShortInfo>, HandleGetCarts>()
.AddQueryHandler<GetCartHistory, IReadOnlyList<CartHistory>, HandleGetCartHistory>()
.AddQueryHandler<GetCartAtVersion, CartDetails, HandleGetCartAtVersion>();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Ardalis.GuardClauses;
using Core.ElasticSearch.Indices;
using Core.Exceptions;
using Core.Queries;
using Nest;

Expand All @@ -26,7 +27,7 @@ public static GetCartById Create(Guid cartId)
}

internal class HandleGetCartById :
IQueryHandler<GetCartById, CartDetails?>
IQueryHandler<GetCartById, CartDetails>
{
private readonly IElasticClient elasticClient;

Expand All @@ -35,13 +36,13 @@ public HandleGetCartById(IElasticClient elasticClient)
this.elasticClient = elasticClient;
}

public async Task<CartDetails?> Handle(GetCartById request, CancellationToken cancellationToken)
public async Task<CartDetails> Handle(GetCartById request, CancellationToken cancellationToken)
{
var result = await elasticClient.GetAsync<CartDetails>(request.CartId,
c => c.Index(IndexNameMapper.ToIndexName<CartDetails>()),
ct: cancellationToken);

return result?.Source;
return result?.Source ?? throw AggregateNotFoundException.For<Cart>(request.CartId);
}
}
}
}
15 changes: 11 additions & 4 deletions CQRS_Flow/.NET/Core/Core.Testing/ApiFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -42,19 +42,26 @@ protected ApiFixture()

public virtual Task DisposeAsync() => Task.CompletedTask;

public async Task<HttpResponseMessage> Get(string path = "", int maxNumberOfRetries = 0, int retryIntervalInMs = 1000)
public async Task<HttpResponseMessage> Get(string path = "", int maxNumberOfRetries = 0,
int retryIntervalInMs = 1000, Func<HttpResponseMessage, ValueTask<bool>>? check = null)
{
HttpResponseMessage queryResponse;
var retryCount = maxNumberOfRetries;

var doCheck = check ?? (response => new(response.StatusCode == HttpStatusCode.OK));
do
{
queryResponse = await Client.GetAsync(
$"{ApiUrl}/{path}"
);

if (queryResponse.StatusCode != HttpStatusCode.OK && retryCount > 0)
Thread.Sleep(retryIntervalInMs);
} while (queryResponse.StatusCode != HttpStatusCode.OK && maxNumberOfRetries-- > 0);
if (retryCount == 0 || (await doCheck(queryResponse)))
break;

await Task.Delay(retryIntervalInMs);
retryCount--;
} while (true);

return queryResponse;
}

Expand Down
19 changes: 19 additions & 0 deletions CQRS_Flow/.NET/Core/Core/Commands/Config.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using MediatR;
using Microsoft.Extensions.DependencyInjection;

namespace Core.Commands
{
public static class Config
{
public static IServiceCollection AddCommandHandler<TCommand, TCommandHandler>(
this IServiceCollection services
)
where TCommand : ICommand
where TCommandHandler : class, ICommandHandler<TCommand>
{
return services.AddTransient<TCommandHandler>()
.AddTransient<IRequestHandler<TCommand, Unit>>(sp => sp.GetRequiredService<TCommandHandler>())
.AddTransient<ICommandHandler<TCommand>>(sp => sp.GetRequiredService<TCommandHandler>());
}
}
}
19 changes: 19 additions & 0 deletions CQRS_Flow/.NET/Core/Core/Events/Config.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using MediatR;
using Microsoft.Extensions.DependencyInjection;

namespace Core.Events
{
public static class Config
{
public static IServiceCollection AddEventHandler<TEvent, TEventResult, TEventHandler>(
this IServiceCollection services
)
where TEvent : IEvent
where TEventHandler : class, IEventHandler<TEvent>
{
return services.AddTransient<TEventHandler>()
.AddTransient<INotificationHandler<TEvent>>(sp => sp.GetRequiredService<TEventHandler>())
.AddTransient<IEventHandler<TEvent>>(sp => sp.GetRequiredService<TEventHandler>());
}
}
}
19 changes: 19 additions & 0 deletions CQRS_Flow/.NET/Core/Core/Queries/Config.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using MediatR;
using Microsoft.Extensions.DependencyInjection;

namespace Core.Queries
{
public static class Config
{
public static IServiceCollection AddQueryHandler<TQuery, TQueryResult, TQueryHandler>(
this IServiceCollection services
)
where TQuery : IQuery<TQueryResult>
where TQueryHandler : class, IQueryHandler<TQuery, TQueryResult>
{
return services.AddTransient<TQueryHandler>()
.AddTransient<IRequestHandler<TQuery, TQueryResult>>(sp => sp.GetRequiredService<TQueryHandler>())
.AddTransient<IQueryHandler<TQuery, TQueryResult>>(sp => sp.GetRequiredService<TQueryHandler>());
}
}
}

0 comments on commit eabdead

Please sign in to comment.