Skip to content
This repository has been archived by the owner on Jan 13, 2025. It is now read-only.

Mediatr example #325

Merged
merged 25 commits into from
Nov 15, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
b68f044
Updates based on documentation
efleming18 Oct 18, 2019
88d23d5
Getting the build passing
efleming18 Oct 18, 2019
4d540b5
Getting app functioning
efleming18 Oct 18, 2019
4fd8123
A few cleanups to confirm it's working as expected
efleming18 Oct 18, 2019
8aec301
Fixing functional tests
efleming18 Oct 18, 2019
bb184b7
Updating dockerfile for 3.0
efleming18 Oct 18, 2019
ebeb9ae
Functional Tests now run sequentially
efleming18 Oct 21, 2019
2a1b588
Updating to latest version of moq
efleming18 Oct 24, 2019
80210ab
Adding migration for post 3.0 upgrades
efleming18 Oct 31, 2019
be79a01
Removing commented out lines
efleming18 Nov 1, 2019
0450c12
Moving address and catalogitemordered configuration in to classes tha…
efleming18 Nov 5, 2019
fff4bea
Installing MediatR nuget packages
efleming18 May 2, 2019
f84f00e
Configure MediatR in Startup
efleming18 May 2, 2019
4c54d63
Creating GetMyOrders MediatR query and handler
efleming18 May 6, 2019
284bfd3
Adding GetOrderDetails MediatR handler
efleming18 May 6, 2019
dddf11b
Refactoring out default values
efleming18 May 6, 2019
d171f85
Added tests for GetOrderDetails mediator handler
efleming18 May 7, 2019
2b30213
Defaulting values on Models for now
efleming18 May 8, 2019
4961c08
Removing some spaces
efleming18 May 8, 2019
5fec64d
Splitting files
efleming18 May 8, 2019
cada9e2
Splitting out the GetOrderDetails files
efleming18 May 8, 2019
df35b4f
Adding test for GetMyOrders
efleming18 May 8, 2019
2c22853
restructuing folders
efleming18 May 23, 2019
0aa9639
Using constant
efleming18 May 23, 2019
4bcb81d
Merge branch 'master' into mediatr-example
efleming18 Nov 7, 2019
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
60 changes: 12 additions & 48 deletions src/Web/Controllers/OrderController.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using Microsoft.AspNetCore.Authorization;
using MediatR;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
using Microsoft.eShopWeb.Web.ViewModels;
using System.Linq;
using Microsoft.eShopWeb.Web.Features.MyOrders;
using Microsoft.eShopWeb.Web.Features.OrderDetails;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.Web.Controllers
Expand All @@ -13,66 +12,31 @@ namespace Microsoft.eShopWeb.Web.Controllers
[Route("[controller]/[action]")]
public class OrderController : Controller
{
private readonly IOrderRepository _orderRepository;
private readonly IMediator _mediator;

public OrderController(IOrderRepository orderRepository)
public OrderController(IMediator mediator)
{
_orderRepository = orderRepository;
_mediator = mediator;
}

[HttpGet()]
public async Task<IActionResult> MyOrders()
{
var orders = await _orderRepository.ListAsync(new CustomerOrdersWithItemsSpecification(User.Identity.Name));
var viewModel = await _mediator.Send(new GetMyOrders(User.Identity.Name));

var viewModel = orders
.Select(o => new OrderViewModel()
{
OrderDate = o.OrderDate,
OrderItems = o.OrderItems?.Select(oi => new OrderItemViewModel()
{
Discount = 0,
PictureUrl = oi.ItemOrdered.PictureUri,
ProductId = oi.ItemOrdered.CatalogItemId,
ProductName = oi.ItemOrdered.ProductName,
UnitPrice = oi.UnitPrice,
Units = oi.Units
}).ToList(),
OrderNumber = o.Id,
ShippingAddress = o.ShipToAddress,
Status = "Pending",
Total = o.Total()

});
return View(viewModel);
}

[HttpGet("{orderId}")]
public async Task<IActionResult> Detail(int orderId)
{
var customerOrders = await _orderRepository.ListAsync(new CustomerOrdersWithItemsSpecification(User.Identity.Name));
var order = customerOrders.FirstOrDefault(o => o.Id == orderId);
if (order == null)
var viewModel = await _mediator.Send(new GetOrderDetails(User.Identity.Name, orderId));

if (viewModel == null)
{
return BadRequest("No such order found for this user.");
}
var viewModel = new OrderViewModel()
{
OrderDate = order.OrderDate,
OrderItems = order.OrderItems.Select(oi => new OrderItemViewModel()
{
Discount = 0,
PictureUrl = oi.ItemOrdered.PictureUri,
ProductId = oi.ItemOrdered.CatalogItemId,
ProductName = oi.ItemOrdered.ProductName,
UnitPrice = oi.UnitPrice,
Units = oi.Units
}).ToList(),
OrderNumber = order.Id,
ShippingAddress = order.ShipToAddress,
Status = "Pending",
Total = order.Total()
};

return View(viewModel);
}
}
Expand Down
16 changes: 16 additions & 0 deletions src/Web/Features/MyOrders/GetMyOrders.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using MediatR;
using Microsoft.eShopWeb.Web.ViewModels;
using System.Collections.Generic;

namespace Microsoft.eShopWeb.Web.Features.MyOrders
{
public class GetMyOrders : IRequest<IEnumerable<OrderViewModel>>
{
public string UserName { get; set; }

public GetMyOrders(string userName)
{
UserName = userName;
}
}
}
43 changes: 43 additions & 0 deletions src/Web/Features/MyOrders/GetMyOrdersHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using MediatR;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
using Microsoft.eShopWeb.Web.ViewModels;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.Web.Features.MyOrders
{
public class GetMyOrdersHandler : IRequestHandler<GetMyOrders, IEnumerable<OrderViewModel>>
{
private readonly IOrderRepository _orderRepository;

public GetMyOrdersHandler(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}

public async Task<IEnumerable<OrderViewModel>> Handle(GetMyOrders request, CancellationToken cancellationToken)
{
var specification = new CustomerOrdersWithItemsSpecification(request.UserName);
var orders = await _orderRepository.ListAsync(specification);

return orders.Select(o => new OrderViewModel
{
OrderDate = o.OrderDate,
OrderItems = o.OrderItems?.Select(oi => new OrderItemViewModel()
{
PictureUrl = oi.ItemOrdered.PictureUri,
ProductId = oi.ItemOrdered.CatalogItemId,
ProductName = oi.ItemOrdered.ProductName,
UnitPrice = oi.UnitPrice,
Units = oi.Units
}).ToList(),
OrderNumber = o.Id,
ShippingAddress = o.ShipToAddress,
Total = o.Total()
});
}
}
}
17 changes: 17 additions & 0 deletions src/Web/Features/OrderDetails/GetOrderDetails.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
using MediatR;
using Microsoft.eShopWeb.Web.ViewModels;

namespace Microsoft.eShopWeb.Web.Features.OrderDetails
{
public class GetOrderDetails : IRequest<OrderViewModel>
{
public string UserName { get; set; }
public int OrderId { get; set; }

public GetOrderDetails(string userName, int orderId)
{
UserName = userName;
OrderId = orderId;
}
}
}
47 changes: 47 additions & 0 deletions src/Web/Features/OrderDetails/GetOrderDetailsHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using MediatR;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.ApplicationCore.Specifications;
using Microsoft.eShopWeb.Web.ViewModels;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Microsoft.eShopWeb.Web.Features.OrderDetails
{
public class GetOrderDetailsHandler : IRequestHandler<GetOrderDetails, OrderViewModel>
{
private readonly IOrderRepository _orderRepository;

public GetOrderDetailsHandler(IOrderRepository orderRepository)
{
_orderRepository = orderRepository;
}

public async Task<OrderViewModel> Handle(GetOrderDetails request, CancellationToken cancellationToken)
{
var customerOrders = await _orderRepository.ListAsync(new CustomerOrdersWithItemsSpecification(request.UserName));
var order = customerOrders.FirstOrDefault(o => o.Id == request.OrderId);

if (order == null)
{
return null;
}

return new OrderViewModel
{
OrderDate = order.OrderDate,
OrderItems = order.OrderItems.Select(oi => new OrderItemViewModel
{
PictureUrl = oi.ItemOrdered.PictureUri,
ProductId = oi.ItemOrdered.CatalogItemId,
ProductName = oi.ItemOrdered.ProductName,
UnitPrice = oi.UnitPrice,
Units = oi.Units
}).ToList(),
OrderNumber = order.Id,
ShippingAddress = order.ShipToAddress,
Total = order.Total()
};
}
}
}
15 changes: 8 additions & 7 deletions src/Web/Startup.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Ardalis.ListStartupServices;
using MediatR;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Diagnostics.HealthChecks;
using Microsoft.AspNetCore.Hosting;
Expand Down Expand Up @@ -81,7 +82,9 @@ public void ConfigureServices(IServiceCollection services)
ConfigureCookieSettings(services);

CreateIdentityIfNotCreated(services);


services.AddMediatR(typeof(BasketViewModelService).Assembly);

services.AddScoped(typeof(IAsyncRepository<>), typeof(EfRepository<>));
services.AddScoped<ICatalogViewModelService, CachedCatalogViewModelService>();
services.AddScoped<IBasketService, BasketService>();
Expand Down Expand Up @@ -109,13 +112,12 @@ public void ConfigureServices(IServiceCollection services)
options.Conventions.Add(new RouteTokenTransformerConvention(
new SlugifyParameterTransformer()));

});
services.AddControllersWithViews();
});
services.AddRazorPages(options =>
{
options.Conventions.AuthorizePage("/Basket/Checkout");
});

services.AddControllersWithViews();
services.AddControllers();

services.AddHttpContextAccessor();
Expand Down Expand Up @@ -207,14 +209,13 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();


app.UseHttpsRedirection();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();

// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger();

Expand Down
7 changes: 1 addition & 6 deletions src/Web/ViewModels/OrderItemViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,10 @@
public class OrderItemViewModel
{
public int ProductId { get; set; }

public string ProductName { get; set; }

public decimal UnitPrice { get; set; }

public decimal Discount { get; set; }

public decimal Discount => 0;
public int Units { get; set; }

public string PictureUrl { get; set; }
}
}
10 changes: 4 additions & 6 deletions src/Web/ViewModels/OrderViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@ namespace Microsoft.eShopWeb.Web.ViewModels
{
public class OrderViewModel
{
private const string DEFAULT_STATUS = "Pending";

public int OrderNumber { get; set; }
public DateTimeOffset OrderDate { get; set; }
public decimal Total { get; set; }
public string Status { get; set; }

public Address ShippingAddress { get; set; }

public string Status => DEFAULT_STATUS;
public Address ShippingAddress { get; set; }
public List<OrderItemViewModel> OrderItems { get; set; } = new List<OrderItemViewModel>();

}

}
3 changes: 3 additions & 0 deletions src/Web/Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@

<ItemGroup>
<PackageReference Include="Ardalis.ListStartupServices" Version="1.1.3" />

<PackageReference Include="MediatR" Version="7.0.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
<PackageReference Include="BuildBundlerMinifier" Version="2.9.406" Condition="'$(Configuration)'=='Release'" PrivateAssets="All" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="3.3.1" />
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="3.0.0" />
Expand Down
38 changes: 38 additions & 0 deletions tests/UnitTests/MediatorHandlers/OrdersTests/GetMyOrders_Should.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using Microsoft.eShopWeb.ApplicationCore.Entities.OrderAggregate;
using Microsoft.eShopWeb.ApplicationCore.Interfaces;
using Microsoft.eShopWeb.Web.Features.MyOrders;
using Moq;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace Microsoft.eShopWeb.UnitTests.MediatorHandlers.OrdersTests
{
public class GetMyOrders_Should
{
private readonly Mock<IOrderRepository> _mockOrderRepository;

public GetMyOrders_Should()
{
var item = new OrderItem(new CatalogItemOrdered(1, "ProductName", "URI"), 10.00m, 10);
var address = new Address(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>(), It.IsAny<string>());
Order order = new Order("buyerId", address, new List<OrderItem> { item });

_mockOrderRepository = new Mock<IOrderRepository>();
_mockOrderRepository.Setup(x => x.ListAsync(It.IsAny<ISpecification<Order>>())).ReturnsAsync(new List<Order> { order });
}

[Fact]
public async Task NotReturnNull_If_OrdersArePresent()
{
var request = new GetMyOrders("SomeUserName");

var handler = new GetMyOrdersHandler(_mockOrderRepository.Object);

var result = await handler.Handle(request, CancellationToken.None);

Assert.NotNull(result);
}
}
}
Loading