Skip to content

Commit

Permalink
commit
Browse files Browse the repository at this point in the history
  • Loading branch information
neozhu committed Jan 9, 2025
1 parent 9d363ec commit 37e9149
Show file tree
Hide file tree
Showing 17 changed files with 376 additions and 116 deletions.
71 changes: 57 additions & 14 deletions src/CleanAspire.Api/Endpoints/ProductEndpointRegistrar.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// This class defines minimal API endpoints related to product management.
// Each endpoint corresponds to a specific command or query defined in the Features layer.
// The purpose is to expose a RESTful interface for interacting with products, delegating request handling to command/query handlers via the Mediator pattern.

// Key points:
// 1. Each endpoint is linked to a command or query, ensuring clear separation between API and application logic.
// 2. Commands represent actions (e.g., CreateProductCommand, DeleteProductCommand).
// 3. Queries retrieve data (e.g., GetAllProductsQuery, GetProductByIdQuery, ProductsWithPaginationQuery).
// 4. Mediator is used to send commands/queries to their respective handlers, enabling a clean and testable architecture.


using CleanAspire.Application.Common.Models;
using CleanAspire.Application.Features.Products.Commands;
Expand All @@ -12,13 +19,25 @@

namespace CleanAspire.Api.Endpoints;

/// <summary>
/// This class defines minimal API endpoints related to product management.
/// Each endpoint corresponds to a specific command or query defined in the Features layer.
/// The purpose is to expose a RESTful interface for interacting with products, delegating request handling to command/query handlers via the Mediator pattern.
/// </summary>
public class ProductEndpointRegistrar(ILogger<ProductEndpointRegistrar> logger) : IEndpointRegistrar
{
/// <summary>
/// Registers the routes for product-related endpoints.
/// </summary>
/// <param name="routes">The route builder to which the endpoints will be added.</param>
public void RegisterRoutes(IEndpointRouteBuilder routes)
{
var group = routes.MapGroup("/products").WithTags("products").RequireAuthorization();

// Get all products
/// <summary>
/// Gets all products.
/// </summary>
/// <returns>A list of all products in the system.</returns>
group.MapGet("/", async ([FromServices] IMediator mediator) =>
{
var query = new GetAllProductsQuery();
Expand All @@ -30,7 +49,11 @@ public void RegisterRoutes(IEndpointRouteBuilder routes)
.WithSummary("Get all products")
.WithDescription("Returns a list of all products in the system.");

// Get product by ID
/// <summary>
/// Gets a product by its ID.
/// </summary>
/// <param name="id">The unique ID of the product.</param>
/// <returns>The details of the specified product.</returns>
group.MapGet("/{id}", (IMediator mediator, [FromRoute] string id) => mediator.Send(new GetProductByIdQuery(id)))
.Produces<ProductDto>(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status404NotFound)
Expand All @@ -39,7 +62,11 @@ public void RegisterRoutes(IEndpointRouteBuilder routes)
.WithSummary("Get product by ID")
.WithDescription("Returns the details of a specific product by its unique ID.");

// Create a new product
/// <summary>
/// Creates a new product.
/// </summary>
/// <param name="command">The command containing the details of the product to create.</param>
/// <returns>The created product.</returns>
group.MapPost("/", ([FromServices] IMediator mediator, [FromBody] CreateProductCommand command) => mediator.Send(command))
.Produces<ProductDto>(StatusCodes.Status201Created)
.ProducesValidationProblem(StatusCodes.Status422UnprocessableEntity)
Expand All @@ -48,7 +75,10 @@ public void RegisterRoutes(IEndpointRouteBuilder routes)
.WithSummary("Create a new product")
.WithDescription("Creates a new product with the provided details.");

// Update an existing product
/// <summary>
/// Updates an existing product.
/// </summary>
/// <param name="command">The command containing the updated details of the product.</param>
group.MapPut("/", ([FromServices] IMediator mediator, [FromBody] UpdateProductCommand command) => mediator.Send(command))
.Produces(StatusCodes.Status204NoContent)
.ProducesValidationProblem(StatusCodes.Status422UnprocessableEntity)
Expand All @@ -58,7 +88,10 @@ public void RegisterRoutes(IEndpointRouteBuilder routes)
.WithSummary("Update an existing product")
.WithDescription("Updates the details of an existing product.");

// Delete products by IDs
/// <summary>
/// Deletes products by their IDs.
/// </summary>
/// <param name="command">The command containing the IDs of the products to delete.</param>
group.MapDelete("/", ([FromServices] IMediator mediator, [FromBody] DeleteProductCommand command) => mediator.Send(command))
.Produces(StatusCodes.Status204NoContent)
.ProducesValidationProblem(StatusCodes.Status422UnprocessableEntity)
Expand All @@ -68,15 +101,23 @@ public void RegisterRoutes(IEndpointRouteBuilder routes)
.WithSummary("Delete products by IDs")
.WithDescription("Deletes one or more products by their unique IDs.");

// Get products with pagination and filtering
/// <summary>
/// Gets products with pagination and filtering.
/// </summary>
/// <param name="query">The query containing pagination and filtering parameters.</param>
/// <returns>A paginated list of products.</returns>
group.MapPost("/pagination", ([FromServices] IMediator mediator, [FromBody] ProductsWithPaginationQuery query) => mediator.Send(query))
.Produces<PaginatedResult<ProductDto>>(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status400BadRequest)
.ProducesProblem(StatusCodes.Status500InternalServerError)
.WithSummary("Get products with pagination")
.WithDescription("Returns a paginated list of products based on search keywords, page size, and sorting options.");

// Export products to CSV
/// <summary>
/// Exports products to a CSV file.
/// </summary>
/// <param name="keywords">The keywords to filter the products.</param>
/// <returns>A CSV file containing the product data.</returns>
group.MapGet("/export", async ([FromQuery] string keywords, [FromServices] IMediator mediator) =>
{
var result = await mediator.Send(new ExportProductsQuery(keywords));
Expand All @@ -89,7 +130,12 @@ public void RegisterRoutes(IEndpointRouteBuilder routes)
.WithSummary("Export Products to CSV")
.WithDescription("Exports the product data to a CSV file based on the provided keywords. The CSV file includes product details such as ID, name, description, price, SKU, and category.");

// Import products from CSV
/// <summary>
/// Imports products from CSV files.
/// </summary>
/// <param name="request">The request containing the CSV files to import.</param>
/// <param name="context">The HTTP context.</param>
/// <returns>A list of responses for each imported file.</returns>
group.MapPost("/import", async ([FromForm] FileUploadRequest request, HttpContext context, [FromServices] IMediator mediator) =>
{
var response = new List<FileUploadResponse>();
Expand Down Expand Up @@ -130,9 +176,6 @@ public void RegisterRoutes(IEndpointRouteBuilder routes)
.WithMetadata(new ConsumesAttribute("multipart/form-data"))
.WithSummary("Import Products from CSV")
.WithDescription("Imports product data from one or more CSV files. The CSV files should contain product details in the required format.");

}


}

Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
/*This code defines a CreateProductCommand and its handler to create a new product within a CQRS architecture.
* The CreateProductCommand encapsulates necessary data such as SKU, Name, Category, Description, Price, Currency, and UOM.
* It implements IFusionCacheRefreshRequest<ProductDto> for cache updates and IRequiresValidation for data validation. The handler,
* CreateProductCommandHandler, processes the command by creating a Product entity,
* adding a ProductCreatedEvent domain event, saving the product to the database via IApplicationDbContext,
* and returning a ProductDto with the product's basic information.
* This design ensures clear separation of concerns and maintainability.
*/
// This code defines a command and its handler for creating a new product in the database.
// The CreateProductCommand encapsulates the required data to create a product.
// The CreateProductCommandHandler processes the command, creates a product entity, adds a domain event, saves it to the database, and returns a ProductDto.


// Using directives for necessary namespaces, bringing in external types used in the code
using CleanAspire.Application.Features.Products.DTOs; // Contains data transfer objects (DTOs) for the Product feature
Expand All @@ -17,8 +13,8 @@ namespace CleanAspire.Application.Features.Products.Commands;

// A record that defines the CreateProductCommand, which encapsulates the data needed to create a new product
public record CreateProductCommand(
string SKU, // The stock-keeping unit, a unique identifier for the product
string Name, // The name of the product
string SKU, // The stock-keeping unit, a unique identifier for the product, corresponding to the SKU field in ProductDto
string Name, // The name of the product, corresponding to the SKU field in ProductDto
ProductCategoryDto? Category, // The category of the product, nullable, referencing ProductCategoryDto definition
string? Description, // A description of the product, nullable, corresponding to the Description field in ProductDto
decimal Price, // The price of the product, matching the Price field in ProductDto
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
// A record that defines the DeleteProductCommand, which encapsulates the data needed to delete products by their IDs
using CleanAspire.Application.Features.Products.EventHandlers;
using CleanAspire.Application.Pipeline;
// This code defines a command and its handler for deleting products from a database.
// The DeleteProductCommand encapsulates product IDs, supporting cache refresh and validation.
// The DeleteProductCommandHandler processes the command, removes products, triggers domain events, and saves changes.


// A record that defines the DeleteProductCommand, which encapsulates the data needed to delete products by their IDs
using CleanAspire.Application.Features.Products.EventHandlers; // Contains event handlers related to Product events
using CleanAspire.Application.Pipeline; // Contains pipeline behaviors and related interfaces

// Namespace for organizing related classes and features
namespace CleanAspire.Application.Features.Products.Commands;

public record DeleteProductCommand(params IEnumerable<string> Ids) // Takes a list of product IDs as parameters
: IFusionCacheRefreshRequest<Unit>, // Implements interface for cache refresh requests
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
using System.Globalization;
// This code defines a command and its handler for importing products from a CSV file.
// The ImportProductsCommand encapsulates the input stream, supporting cache refresh and validation.
// The ImportProductsCommandHandler processes the command, reads CSV data, maps to entities, adds domain events, and saves changes.


using System.Globalization;
using CleanAspire.Application.Features.Products.DTOs;
using CleanAspire.Application.Features.Products.EventHandlers;
using CleanAspire.Application.Pipeline;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,7 @@
/*
This C# code defines a feature for updating product information in an application using the Clean Architecture principles. It includes:
UpdateProductCommand: A record that encapsulates the data needed to update a product.
Implements IFusionCacheRefreshRequest<Unit> and IRequiresValidation interfaces for cache refresh and validation support.
Includes properties such as Id, SKU, Name, Category, etc.
UpdateProductCommandHandler: A handler class that processes the UpdateProductCommand.
Uses IApplicationDbContext to interact with the database.
Updates the product details, raises a domain event (ProductUpdatedEvent), and persists changes to the database.
*/
// This code defines a command and its handler for updating product details in the database.
// The UpdateProductCommand encapsulates the necessary data to update a product.
// The UpdateProductCommandHandler processes the command, validates existence, updates product details, triggers a domain event, and saves changes.


using CleanAspire.Application.Features.Products.DTOs; // Import DTOs related to products.
using CleanAspire.Application.Features.Products.EventHandlers; // Import event handlers for product-related events.
Expand All @@ -16,14 +11,14 @@ Uses IApplicationDbContext to interact with the database.

// A record representing the command to update a product.
public record UpdateProductCommand(
string Id, // Product ID.
string SKU, // Stock Keeping Unit, a unique identifier for the product.
string Name, // Product name.
ProductCategoryDto? Category, // Optional category of the product.
string? Description, // Optional product description.
decimal Price, // Product price.
string? Currency, // Optional currency code (e.g., USD, EUR).
string? UOM // Optional Unit of Measurement (e.g., kg, pcs).
string Id, // Product ID. corresponding to the Id field in ProductDto
string SKU, // Stock Keeping Unit, a unique identifier for the product. corresponding to the SKU field in ProductDto
string Name, // Product name. corresponding to the Name field in ProductDto
ProductCategoryDto? Category, // Optional category of the product. corresponding to the ProductCategoryDto field in ProductDto
string? Description, // Optional product description. corresponding to the Description field in ProductDto
decimal Price, // Product price. corresponding to the Price field in ProductDto
string? Currency, // Optional currency code (e.g., USD, EUR). corresponding to the Currency field in ProductDto
string? UOM // Optional Unit of Measurement (e.g., kg, pcs). corresponding to the UOM field in ProductDto
) : IFusionCacheRefreshRequest<Unit>, // Interface for cache refresh requests.
IRequiresValidation // Interface indicating that the command requires validation.
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* This section defines a Data Transfer Object (DTO) and an enumeration to represent product information and its possible categories.
* These classes are used to simplify and standardize how data is transferred between different layers of the application, ensuring separation of concerns and maintainability.
*/
// This code defines a data transfer object (DTO) for products and an enumeration for product categories.
// The ProductDto class encapsulates product details for data transfer between application layers.
// The ProductCategoryDto enum provides predefined categories for products.

namespace CleanAspire.Application.Features.Products.DTOs; // Define the namespace for product-related DTOs.

// A DTO representing a product, used to transfer data between application layers.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,31 @@
namespace CleanAspire.Application.Features.Products.EventHandlers;
// Namespace: CleanAspire.Application.Features.Products.EventHandlers
// Class: ProductCreatedEvent
// Inherits from: DomainEvent, representing a base class for domain events

namespace CleanAspire.Application.Features.Products.EventHandlers;
/// <summary>
/// Represents an event triggered when a product is created.
/// Purpose:
/// 1. To signal the creation of a product.
/// 2. Used in the domain event notification mechanism to pass product details to subscribers.
/// </summary>
public class ProductCreatedEvent : DomainEvent
{
/// <summary>
/// Constructor to initialize the event and pass the created product instance.
/// </summary>
/// <param name="item">The created product instance.</param>
public ProductCreatedEvent(Product item)
{
Item = item;
Item = item; // Assigns the provided product instance to the read-only property
}

/// <summary>
/// Gets the product instance associated with the event.
/// </summary>
public Product Item { get; }
}

/*
public class ProductCreatedEventHandler : INotificationHandler<ProductCreatedEvent>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,32 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// Namespace: CleanAspire.Application.Features.Products.EventHandlers
// Class: ProductDeletedEvent
// Inherits from: DomainEvent, representing a base class for domain events

namespace CleanAspire.Application.Features.Products.EventHandlers;
/// <summary>
/// Represents an event triggered when a product is deleted.
/// Purpose:
/// 1. To signal the deletion of a product.
/// 2. Used in the domain event notification mechanism to inform subscribers about the deleted product.
/// </summary>
public class ProductDeletedEvent : DomainEvent
{
/// <summary>
/// Constructor to initialize the event and pass the deleted product instance.
/// </summary>
/// <param name="item">The deleted product instance.</param>
public ProductDeletedEvent(Product item)
{
Item = item;
Item = item; // Assigns the provided product instance to the read-only property
}

/// <summary>
/// Gets the product instance associated with the event.
/// </summary>
public Product Item { get; }
}


/*
public class ProductDeletedEventHandler : INotificationHandler<ProductDeletedEvent>
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,28 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
// Namespace: CleanAspire.Application.Features.Products.EventHandlers
// Class: ProductUpdatedEvent
// Inherits from: DomainEvent, representing a base class for domain events

namespace CleanAspire.Application.Features.Products.EventHandlers;
/// <summary>
/// Represents an event triggered when a product is updated.
/// Purpose:
/// 1. To signal that a product has been updated.
/// 2. Used in the domain event notification mechanism to inform subscribers about the updated product details.
/// </summary>
public class ProductUpdatedEvent : DomainEvent
{
/// <summary>
/// Constructor to initialize the event and pass the updated product instance.
/// </summary>
/// <param name="item">The updated product instance.</param>
public ProductUpdatedEvent(Product item)
{
Item = item;
Item = item; // Assigns the provided product instance to the read-only property
}

/// <summary>
/// Gets the product instance associated with the event.
/// </summary>
public Product Item { get; }
}
/*
Expand Down
Loading

0 comments on commit 37e9149

Please sign in to comment.