Skip to content

Commit

Permalink
Merge pull request #40 from neozhu/improve/cleancode1
Browse files Browse the repository at this point in the history
Integrate ChatGPT Explore GPTs for Standardized Code Generation
  • Loading branch information
neozhu authored Jan 9, 2025
2 parents cbe2c7e + ff9eca7 commit a87f2b6
Show file tree
Hide file tree
Showing 25 changed files with 784 additions and 180 deletions.
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ By incorporating robust offline capabilities, CleanAspire empowers developers to
- Uses **IndexedDB** to cache data locally, allowing the application to retrieve data and function offline.
- The system detects the online/offline status and fetches data from **IndexedDB** when offline, ensuring uninterrupted access to key features.

### How to generate code use Chat GPTs
![chatgpt](./gpts.png)
[Link to CleanAspire Code Generator](https://chatgpt.com/g/g-677e40252ff88191933bb84567b62e7b-cleanaspire-code-generator)

### How to Create a New Object in a CRUD Application: A Step-by-Step Guide

Expand Down
Binary file added gpts.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 13 additions & 3 deletions src/CleanAspire.Api/Endpoints/IEndpointRegistrar.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
// 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 namespace contains utilities for defining and registering API endpoint routes in a minimal API setup.

// Purpose:
// 1. **`IEndpointRegistrar` Interface**:
// - Provides a contract for defining endpoint registration logic.
// - Ensures consistency across all endpoint registration implementations by enforcing a common method (`RegisterRoutes`).

namespace CleanAspire.Api.Endpoints;

/// <summary>
/// Defines a contract for registering endpoint routes.
/// </summary>
public interface IEndpointRegistrar
{
/// <summary>
/// Registers the routes for the application.
/// </summary>
/// <param name="routes">The <see cref="IEndpointRouteBuilder"/> to add routes to.</param>
void RegisterRoutes(IEndpointRouteBuilder routes);
}
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,46 +1,68 @@
using CleanAspire.Application.Features.Products.DTOs;
using CleanAspire.Application.Features.Products.EventHandlers;
using CleanAspire.Application.Pipeline;
// 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
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;

// A record that defines the CreateProductCommand, which encapsulates the data needed to create a new product
public record CreateProductCommand(
string SKU,
string Name,
ProductCategoryDto? Category,
string? Description,
decimal Price,
string? Currency,
string? UOM
) : IFusionCacheRefreshRequest<ProductDto>, IRequiresValidation
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
string? Currency, // The currency of the price, nullable, referencing the Currency field in ProductDto
string? UOM // The unit of measure for the product, nullable, consistent with the UOM field in ProductDto
) : IFusionCacheRefreshRequest<ProductDto>, // Implements interface for cache refresh requests
IRequiresValidation // Implements interface for validation requirements
{
// Optional tags for categorizing the command, useful for logging or debugging
public IEnumerable<string>? Tags => new[] { "products" };
}

// Handler class responsible for processing the CreateProductCommand and returning the result
public class CreateProductCommandHandler : IRequestHandler<CreateProductCommand, ProductDto>
{
private readonly IApplicationDbContext _context;
private readonly IApplicationDbContext _context; // Database context for interacting with the data layer

// Constructor to inject dependencies
public CreateProductCommandHandler(IApplicationDbContext context)
{
_context = context;
}

// Asynchronously handles the CreateProductCommand
public async ValueTask<ProductDto> Handle(CreateProductCommand request, CancellationToken cancellationToken)
{
// Creates a new Product entity using the data from the command
var product = new Product
{
SKU = request.SKU,
Name = request.Name,
Category = (ProductCategory)request.Category,
Description = request.Description,
Price = request.Price,
Currency = request.Currency,
UOM = request.UOM
SKU = request.SKU, // Assigns SKU from command
Name = request.Name, // Assigns Name from command
Category = (ProductCategory)request.Category, // Maps Category DTO to domain entity

Check warning on line 49 in src/CleanAspire.Application/Features/Products/Commands/CreateProductCommand.cs

View workflow job for this annotation

GitHub Actions / build

Nullable value type may be null.
Description = request.Description, // Assigns Description from command
Price = request.Price, // Assigns Price from command
Currency = request.Currency, // Assigns Currency from command
UOM = request.UOM // Assigns Unit of Measure from command
};

// Adds a domain event to signal that a new product has been created
product.AddDomainEvent(new ProductCreatedEvent(product));

// Adds the new product to the database context
_context.Products.Add(product);

// Saves changes asynchronously to the database
await _context.SaveChangesAsync(cancellationToken);

// Returns a ProductDto containing essential information about the created product
return new ProductDto() { Id = product.Id, Name = product.Name, SKU = product.SKU };
}
}
Original file line number Diff line number Diff line change
@@ -1,35 +1,51 @@
// 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 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.

using CleanAspire.Application.Features.Products.EventHandlers;
using CleanAspire.Application.Pipeline;

// 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) : IFusionCacheRefreshRequest<Unit>, IRequiresValidation

public record DeleteProductCommand(params IEnumerable<string> Ids) // Takes a list of product IDs as parameters
: IFusionCacheRefreshRequest<Unit>, // Implements interface for cache refresh requests
IRequiresValidation // Implements interface for validation requirements
{
// Optional tags for categorizing the command, useful for logging or debugging
public IEnumerable<string>? Tags => new[] { "products" };
}


// Handler class responsible for processing the DeleteProductCommand
public class DeleteProductCommandHandler : IRequestHandler<DeleteProductCommand>
{
private readonly IApplicationDbContext _dbContext;
private readonly IApplicationDbContext _dbContext; // Database context for interacting with the data layer

// Constructor to inject dependencies, including logger and database context
public DeleteProductCommandHandler(ILogger<DeleteProductCommandHandler> logger, IApplicationDbContext dbContext)
{
_dbContext = dbContext;
}

// Asynchronously handles the DeleteProductCommand
public async ValueTask<Unit> Handle(DeleteProductCommand request, CancellationToken cancellationToken)
{
// Retrieves products from the database that match the provided IDs
var products = _dbContext.Products.Where(p => request.Ids.Contains(p.Id));

// Iterates through each product to add a deletion domain event and remove it from the database context
foreach (var product in products)
{
product.AddDomainEvent(new ProductDeletedEvent(product));
_dbContext.Products.Remove(product);
product.AddDomainEvent(new ProductDeletedEvent(product)); // Adds domain event for product deletion
_dbContext.Products.Remove(product); // Removes product from the database
}

// Saves changes asynchronously to the database
await _dbContext.SaveChangesAsync(cancellationToken);

// Returns a Unit value to signal successful completion
return Unit.Value;
}
}
Loading

0 comments on commit a87f2b6

Please sign in to comment.