Skip to content

Commit

Permalink
feat: refactor cart functionality and update exception handling
Browse files Browse the repository at this point in the history
  • Loading branch information
baotoq committed Dec 10, 2024
1 parent a2571f6 commit 0092f44
Show file tree
Hide file tree
Showing 23 changed files with 140 additions and 62 deletions.
Original file line number Diff line number Diff line change
@@ -1,6 1,5 @@
using System.ComponentModel.DataAnnotations;
using MicroCommerce.ApiService.Domain.Common;
using MicroCommerce.ApiService.Exceptions;

namespace MicroCommerce.ApiService.Domain.Entities;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 2,8 @@
using FluentValidation;
using MediatR;
using MicroCommerce.ApiService.Domain.Entities;
using MicroCommerce.ApiService.Exceptions;
using MicroCommerce.ApiService.Infrastructure;
using MicroCommerce.ApiService.Infrastructure.Exceptions;
using MicroCommerce.ApiService.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
Expand Down
21 changes: 20 additions & 1 deletion code/src/MicroCommerce.ApiService/Features/Carts/GetCart.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 23,16 @@ public record Response
{
public Guid Id { get; init; }
public CartStatus Status { get; init; }

public IList<CartItemViewModel> CartItems { get; init; } = new List<CartItemViewModel>();
}

public record CartItemViewModel
{
public Guid Id { get; init; }
public string Name { get; init; } = "";
public decimal Price { get; init; }
public string ImageUrl { get; set; } = "";
}

public class Handler : IRequestHandler<Query, Response>
Expand All @@ -37,6 47,8 @@ public Handler(ApplicationDbContext context)
public async Task<Response> Handle(Query request, CancellationToken cancellationToken)
{
var cart = await _context.Carts
.Include(cart => cart.CartItems)
.ThenInclude(cartItem => cartItem.Product)
.FirstOrDefaultAsync(s => s.Id == request.Id, cancellationToken);

if (cart is null)
Expand All @@ -47,7 59,14 @@ public async Task<Response> Handle(Query request, CancellationToken cancellation
return new Response
{
Id = cart.Id,
Status = cart.Status
Status = cart.Status,
CartItems = cart.CartItems.Select(s => new CartItemViewModel
{
Id = s.Product.Id,
Name = s.Product.Name,
Price = s.Product.Price,
ImageUrl = s.Product.ImageUrl
}).ToList()
};
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 2,9 @@
using FluentValidation;
using MediatR;
using MicroCommerce.ApiService.Domain.Entities;
using MicroCommerce.ApiService.Exceptions;
using MicroCommerce.ApiService.Features.DomainEvents;
using MicroCommerce.ApiService.Infrastructure;
using MicroCommerce.ApiService.Infrastructure.Exceptions;
using MicroCommerce.ApiService.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 2,8 @@
using FluentValidation;
using MediatR;
using MicroCommerce.ApiService.Domain.Entities;
using MicroCommerce.ApiService.Exceptions;
using MicroCommerce.ApiService.Infrastructure;
using MicroCommerce.ApiService.Infrastructure.Exceptions;
using MicroCommerce.ApiService.Services;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 7,8 @@
using MassTransit;
using MediatR;
using MicroCommerce.ApiService.Domain.Entities;
using MicroCommerce.ApiService.Exceptions;
using MicroCommerce.ApiService.Infrastructure.Behaviour;
using MicroCommerce.ApiService.Infrastructure.Exceptions;
using MicroCommerce.ApiService.Infrastructure.Interceptors;
using MicroCommerce.ApiService.Services;
using MicroCommerce.ApiService.Services.Elasticsearch;
Expand Down Expand Up @@ -99,7 99,10 @@ private static void AddEfCore(this IHostApplicationBuilder builder)
options.AddInterceptors(sp.GetServices<DispatchDomainEventsInterceptor>());
options.AddInterceptors(sp.GetServices<IndexProductInterceptor>());
});
builder.EnrichNpgsqlDbContext<ApplicationDbContext>();
builder.EnrichNpgsqlDbContext<ApplicationDbContext>(s =>
{
s.DisableRetry = true;
});
}

private static void AddElasticsearch(this IHostApplicationBuilder builder)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 1,6 @@
using System.Diagnostics;
using MediatR;
using MicroCommerce.ApiService.Exceptions;
using MicroCommerce.ApiService.Infrastructure.Exceptions;

namespace MicroCommerce.ApiService.Infrastructure.Behaviour;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 1,7 @@
using System.Diagnostics;
using FluentValidation;
using MediatR;
using MicroCommerce.ApiService.Exceptions;
using MicroCommerce.ApiService.Infrastructure.Exceptions;

namespace MicroCommerce.ApiService.Infrastructure.Behaviour;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 1,4 @@
namespace MicroCommerce.ApiService.Exceptions;
namespace MicroCommerce.ApiService.Infrastructure.Exceptions;

public class CoreException : Exception
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 1,6 @@
using FluentValidation.Results;

namespace MicroCommerce.ApiService.Exceptions;
namespace MicroCommerce.ApiService.Infrastructure.Exceptions;

[Serializable]
public class InvalidValidationException : Exception
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 1,7 @@
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;

namespace MicroCommerce.ApiService.Exceptions;
namespace MicroCommerce.ApiService.Infrastructure.Exceptions;

public class InvalidValidationExceptionHandler : IExceptionHandler
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 2,7 @@
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc;

namespace MicroCommerce.ApiService.Exceptions;
namespace MicroCommerce.ApiService.Infrastructure.Exceptions;

public class NotFoundExceptionHandler : IExceptionHandler
{
Expand Down
25 changes: 25 additions & 0 deletions code/src/MicroCommerce.Web/CartApiClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 1,25 @@
using MicroCommerce.ApiService.Domain.Entities;
using MicroCommerce.ApiService.Features.Carts;

namespace MicroCommerce.Web;

public class CartApiClient(HttpClient httpClient)
{
public async Task<GetCart.Response?> GetCartById(Guid id, CancellationToken cancellationToken = default)
{
var response = await httpClient.GetFromJsonAsync<GetCart.Response>($"/api/carts/{id}", cancellationToken);

return response;
}

public async Task<AddProductToCart.Response?> AddProductToCart(AddProductToCart.Command request, CancellationToken cancellationToken = default)
{
var response = await httpClient.PostAsJsonAsync($"/api/carts/{request.CartId}/products", request, cancellationToken);

response.EnsureSuccessStatusCode();

var data = await response.Content.ReadFromJsonAsync<AddProductToCart.Response>(cancellationToken);

return data;
}
}
11 changes: 0 additions & 11 deletions code/src/MicroCommerce.Web/CartService.cs

This file was deleted.

3 changes: 2 additions & 1 deletion code/src/MicroCommerce.Web/Components/App.razor
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 7,15 @@
<base href="/"/>
<link rel="stylesheet" href="bootstrap/bootstrap.min.css"/>
<link rel="stylesheet" href="app.css"/>
@* ReSharper disable once Html.PathError *@
<link rel="stylesheet" href="MicroCommerce.Web.styles.css"/>
<link rel="icon" type="image/png" href="favicon.png"/>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.1/css/all.min.css" integrity="sha512-5Hs3dF2AEPkpNAR7UiOHba lRSJNeM2ECkwxUIxC1Q/FLycGTbNapWXB4tP889k5T5Ju8fs4b1P5z/iB4nMfSQ==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<HeadOutlet/>
</head>

<body>
<Routes/>
<div id="app">Loading...</div>
<script src="_framework/blazor.web.js"></script>
</body>

Expand Down
23 changes: 21 additions & 2 deletions code/src/MicroCommerce.Web/Components/Layout/MainLayout.razor
Original file line number Diff line number Diff line change
@@ -1,10 1,29 @@
@inherits LayoutComponentBase

<main>
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container-fluid">
<a class="navbar-brand" href="#">My Shop</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<NavLink class="nav-link" href="/">Products</NavLink>
</li>
<li class="nav-item">
<NavLink class="nav-link" href="/cart">Cart</NavLink>
</li>
</ul>
</div>
</div>
</nav>

<main class="mt-4">
@Body
</main>

<div id="blazor-error-ui">
<div id="blazor-error-ui" data-nosnippet>
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 8,12 @@
position: fixed;
width: 100%;
z-index: 1000;
color: red;
}

#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
#blazor-error-ui .dismiss {
cursor: pointer;
position: absolute;
right: 0.75rem;
top: 0.5rem;
}
22 changes: 19 additions & 3 deletions code/src/MicroCommerce.Web/Components/Pages/Cart.razor
Original file line number Diff line number Diff line change
@@ -1,12 1,18 @@
@page "/cart"
@inject CartService CartService
@using MicroCommerce.ApiService.Features.Carts
@using MicroCommerce.ApiService.Features.Products
@inject CartApiClient CartApiClient

<h1 class="text-center my-4">Shopping Cart</h1>
<div class="container">
<ul class="list-group">
@if (CartService.CartItems.Count > 0)
@if (_cart is null)
{
@foreach (var item in CartService.CartItems)
<p>Loading cart…</p>
}
else if (_cart.CartItems.Count > 0)
{
@foreach (var item in _cart.CartItems)
{
<li class="list-group-item d-flex justify-content-between align-items-center">
@item.Name
Expand All @@ -20,3 26,13 @@
}
</ul>
</div>

@code {

GetCart.Response? _cart;

protected override async Task OnInitializedAsync()
{
_cart = await CartApiClient.GetCartById(Guid.NewGuid());
}
}
20 changes: 17 additions & 3 deletions code/src/MicroCommerce.Web/Components/Pages/Home.razor
Original file line number Diff line number Diff line change
@@ -1,6 1,10 @@
@page "/"
@using System.Globalization
@using MicroCommerce.ApiService.Features.Carts
@using MicroCommerce.ApiService.Features.Products
@rendermode InteractiveServer

@inject ProductApiClient ProductApiClient
@inject CartApiClient CartApiClient

<PageTitle>Home</PageTitle>

Expand All @@ -17,7 21,7 @@
@foreach (var product in _products)
{
<div class="col-md-4">
<ProductCard Product="product" AddToCart="p => { }"></ProductCard>
<ProductCard Product="product" AddToCart="AddToCart"></ProductCard>
</div>

}
Expand All @@ -27,10 31,20 @@

@code {

GetProductsResponse.Product[]? _products;
GetProductsFromElasticsearch.ProductViewModel[]? _products;

protected override async Task OnInitializedAsync()
{
_products = await ProductApiClient.GetProductsAsync();
}

private async Task AddToCart(GetProductsFromElasticsearch.ProductViewModel product)
{
await CartApiClient.AddProductToCart(new AddProductToCart.Command
{
ProductId = product.Id,
Quantity = 1,
CartId = Guid.NewGuid()
});
}
}
Original file line number Diff line number Diff line change
@@ -1,4 1,5 @@
@using System.Globalization
@using MicroCommerce.ApiService.Features.Products

<div class="card mb-4 h-100">
<img src="images/@Product.ImageUrl" class="card-img-top" alt="@Product.Name">
Expand All @@ -12,6 13,6 @@
</div>

@code {
[Parameter] public required GetProductsResponse.Product Product { get; set; }
[Parameter] public required Action<GetProductsResponse.Product> AddToCart { get; set; }
[Parameter] public required GetProductsFromElasticsearch.ProductViewModel Product { get; set; }
[Parameter] public required Func<GetProductsFromElasticsearch.ProductViewModel, Task> AddToCart { get; set; }
}
1 change: 1 addition & 0 deletions code/src/MicroCommerce.Web/MicroCommerce.Web.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 13,7 @@


<ItemGroup>
<ProjectReference Include="..\MicroCommerce.ApiService\MicroCommerce.ApiService.csproj" />
<ProjectReference Include="..\MicroCommerce.ServiceDefaults\MicroCommerce.ServiceDefaults.csproj" />
</ItemGroup>

Expand Down
21 changes: 5 additions & 16 deletions code/src/MicroCommerce.Web/ProductApiClient.cs
Original file line number Diff line number Diff line change
@@ -1,25 1,14 @@
using MicroCommerce.ApiService.Features.Products;

namespace MicroCommerce.Web;

public class ProductApiClient(HttpClient httpClient)
{
public async Task<GetProductsResponse.Product[]?> GetProductsAsync(int maxItems = 10, CancellationToken cancellationToken = default)
public async Task<GetProductsFromElasticsearch.ProductViewModel[]> GetProductsAsync(int maxItems = 10, CancellationToken cancellationToken = default)
{
var response = await httpClient.GetFromJsonAsync<GetProductsResponse>("/api/products", cancellationToken);
var response = await httpClient.GetFromJsonAsync<GetProductsFromElasticsearch.Response>("/api/products", cancellationToken);

return response?.Data ?? [];
return response?.Data.ToArray() ?? [];
}
}

public record GetProductsResponse
{
public Product[]? Data { get; init; } = [];

public record Product
{
public Guid Id { get; init; }
public string Name { get; init; } = "";
public decimal Price { get; init; }
public long RemainingStock { get; init; }
public string ImageUrl { get; set; } = "";
}
}
Loading

0 comments on commit 0092f44

Please sign in to comment.