feat: Added Product controller and endpoints (Service Layer)

This commit is contained in:
2025-08-03 18:56:47 -06:00
parent fc093e8bff
commit c446fa652e
19 changed files with 654 additions and 2 deletions

View File

@@ -0,0 +1,187 @@
using Asp.Versioning;
using Core.Adapters.Lib.Inventory;
using Core.Inventory.Application.UseCases.Product.Input;
using Core.Inventory.Application.UseCases.Product.Ports;
using Lib.Architecture.BuildingBlocks;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace Core.Inventory.Service.API.Controllers
{
/// <summary>
/// Handles all services and business rules related to <see cref="ProductController"/>.
/// </summary>
[ApiVersion("1.0")]
[Route("api/v{api-version:apiVersion}/[controller]")]
[Produces("application/json")]
[ApiController]
[AllowAnonymous]
public class ProductController : ControllerBase
{
private readonly IComponentHandler<GetProductRequest> getProductHandler;
private readonly IComponentHandler<GetAllProductsRequest> getAllProductsHandler;
private readonly IComponentHandler<GetAllProductsByListRequest> getAllProductsByListHandler;
private readonly IComponentHandler<CreateProductRequest> createProductHandler;
private readonly IComponentHandler<UpdateProductRequest> updateProductHandler;
private readonly IComponentHandler<ChangeProductStatusRequest> changeProductStatusHandler;
private readonly IProductPort port;
/// <summary>
/// Handles all services and business rules related to <see cref="ProductController"/>.
/// </summary>
public ProductController(
IComponentHandler<GetProductRequest> getProductHandler,
IComponentHandler<GetAllProductsRequest> getAllProductsHandler,
IComponentHandler<GetAllProductsByListRequest> getAllProductsByListHandler,
IComponentHandler<CreateProductRequest> createProductHandler,
IComponentHandler<UpdateProductRequest> updateProductHandler,
IComponentHandler<ChangeProductStatusRequest> changeProductStatusHandler,
IProductPort port
)
{
this.createProductHandler = createProductHandler;
this.updateProductHandler = updateProductHandler;
this.changeProductStatusHandler = changeProductStatusHandler;
this.getAllProductsHandler = getAllProductsHandler;
this.getProductHandler = getProductHandler;
this.getAllProductsByListHandler = getAllProductsByListHandler;
this.port = port;
}
/// <summary>
/// Gets all the Products.
/// </summary>
[HttpGet("GetAll")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)]
[ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> GetAllProductsAsync(CancellationToken cancellationToken)
{
await getAllProductsHandler.ExecuteAsync(new GetAllProductsRequest { }, cancellationToken).ConfigureAwait(false);
return port.ViewModel;
}
/// <summary>
/// Gets all the Products by Product identifiers.
/// </summary>
/// <param name="request">The request containing the list of Product identifiers.</param>
/// <param name="cancellationToken">Cancellation token for the asynchronous operation.</param>
/// <returns>The <see cref="IActionResult"/> representing the result of the service call.</returns>
/// <response code="200">The Products found.</response>
/// <response code="204">No content if no Products are found.</response>
/// <response code="400">Bad request if the Product identifiers are missing or invalid.</response>
/// <response code="401">Unauthorized if the user is not authenticated.</response>
/// <response code="412">Precondition failed if the request does not meet expected conditions.</response>
/// <response code="422">Unprocessable entity if the request cannot be processed.</response>
/// <response code="500">Internal server error if an unexpected error occurs.</response>
[HttpPost]
[Route("GetProductList")]
[ProducesResponseType(typeof(IEnumerable<ProductAdapter>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)]
[ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> GetAllProductsByListAsync([FromBody] GetAllProductsByListRequest request, CancellationToken cancellationToken)
{
if (request == null || request.Products == null || !request.Products.Any())
{
return BadRequest("Product identifiers are required.");
}
await getAllProductsByListHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
return port.ViewModel;
}
/// <summary>
/// Gets the Product by identifier.
/// </summary>
[HttpPost]
[Route("GetById")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)]
[ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> GetProductById([FromBody] GetProductRequest request, CancellationToken cancellationToken)
{
if (request.Id == null || !request.Id.Any())
{
return BadRequest("Invalid Product Id");
}
await getProductHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
return port.ViewModel;
}
/// <summary>
/// Creates a new Product.
/// </summary>
[HttpPost("Create")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)]
[ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateProductAsync([FromBody] CreateProductRequest newProduct, CancellationToken cancellationToken = default)
{
await createProductHandler.ExecuteAsync(newProduct, cancellationToken).ConfigureAwait(false);
return port.ViewModel;
}
/// <summary>
/// Updates a full Product by identifier.
/// </summary>
[HttpPut("Update")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)]
[ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> UpdateProductAsync([FromBody] UpdateProductRequest request, CancellationToken cancellationToken = default)
{
await updateProductHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
return port.ViewModel;
}
/// <summary>
/// Changes the status of the Product.
/// </summary>
[HttpPatch]
[Route("ChangeStatus")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status204NoContent)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status401Unauthorized)]
[ProducesResponseType(typeof(Notification), StatusCodes.Status412PreconditionFailed)]
[ProducesResponseType(typeof(Notification), StatusCodes.Status422UnprocessableEntity)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> ChangeProductStatusAsync([FromBody] ChangeProductStatusRequest request,
CancellationToken cancellationToken)
{
if (string.IsNullOrEmpty(request.Id)) { return BadRequest("Invalid Product identifier"); }
await changeProductStatusHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
return port.ViewModel;
}
}
}