feat: added endpoint DeleteProduct

- fix: adapters package updated
- fix: status property renamed
This commit is contained in:
2025-08-05 22:14:27 -06:00
parent ab3863943d
commit b529d905b1
13 changed files with 133 additions and 8 deletions

View File

@@ -0,0 +1,7 @@
namespace Core.Inventory.Application.UseCases.Product.Adapter
{
public class DeleteProductResponseAdapter
{
public bool Success { get; init; }
}
}

View File

@@ -15,5 +15,9 @@ namespace Core.Inventory.Application.UseCases.Product.Adapter
{
ViewModel = new OkObjectResult(output);
}
public void Success(DeleteProductResponseAdapter output)
{
ViewModel = new OkObjectResult(output);
}
}
}

View File

@@ -7,7 +7,7 @@ namespace Core.Inventory.Application.UseCases.Product.Input
public string TenantId { get; set; } = null!;
public string ProductName { get; set; } = null!;
public string Description { get; set; } = null!;
public string Status { get; set; } = null!;
public string ProductStatus { get; set; } = null!;
public List<string> TagIds { get; set; } = new List<string>();
public bool Validate()

View File

@@ -0,0 +1,14 @@
using Lib.Architecture.BuildingBlocks;
namespace Core.Inventory.Application.UseCases.Product.Input
{
public class DeleteProductRequest : Notificator, ICommand
{
public string Id { get; set; } = null!;
public bool Validate()
{
return Id != null;
}
}
}

View File

@@ -9,7 +9,7 @@ namespace Core.Inventory.Application.UseCases.Product.Input
public string TenantId { get; set; } = null!;
public string ProductName { get; set; } = null!;
public string Description { get; set; } = null!;
public string Status { get; set; } = null!;
public string ProductStatus { get; set; } = null!;
public List<string> TagIds { get; set; } = new List<string>();
public bool Validate()

View File

@@ -1,4 +1,5 @@
using Core.Adapters.Lib.Inventory;
using Core.Inventory.Application.UseCases.Product.Adapter;
using Lib.Architecture.BuildingBlocks;
namespace Core.Inventory.Application.UseCases.Product.Ports
@@ -6,6 +7,7 @@ namespace Core.Inventory.Application.UseCases.Product.Ports
public interface IProductPort : IBasePort,
ICommandSuccessPort<ProductAdapter>,
ICommandSuccessPort<List<ProductAdapter>>,
ICommandSuccessPort<DeleteProductResponseAdapter>,
INoContentPort, IBusinessErrorPort, ITimeoutPort, IValidationErrorPort,
INotFoundPort, IForbiddenPort, IUnauthorizedPort, IInternalServerErrorPort,
IBadRequestPort

View File

@@ -1,4 +1,5 @@
using Core.Adapters.Lib.Inventory;
using Core.Inventory.Application.UseCases.Product.Adapter;
using Core.Inventory.Application.UseCases.Product.Input;
using Core.Inventory.Application.UseCases.Product.Ports;
using Core.Inventory.External.Clients;
@@ -15,13 +16,15 @@ namespace Core.Inventory.Application.UseCases.Product
IComponentHandler<GetAllProductsByListRequest>,
IComponentHandler<UpdateProductRequest>,
IComponentHandler<GetProductRequest>,
IComponentHandler<CreateProductRequest>
IComponentHandler<CreateProductRequest>,
IComponentHandler<DeleteProductRequest>
{
private readonly IProductPort _port;
private readonly IValidator<ChangeProductStatusRequest> _changeProductStatusValidator;
private readonly IValidator<CreateProductRequest> _registerProductValidator;
private readonly IValidator<UpdateProductRequest> _updateProductValidator;
private readonly IValidator<GetAllProductsByListRequest> _productsByListValidator;
private readonly IValidator<DeleteProductRequest> _deleteProductValidator;
private readonly IInventoryServiceClient _inventoryServiceClient;
public ProductHandler(
@@ -30,6 +33,7 @@ namespace Core.Inventory.Application.UseCases.Product
IValidator<CreateProductRequest> registerProductValidator,
IValidator<UpdateProductRequest> updateProductValidator,
IValidator<GetAllProductsByListRequest> productsByListValidator,
IValidator<DeleteProductRequest> deleteProductValidator,
IInventoryServiceClient inventoryDALService)
{
_port = port ?? throw new ArgumentNullException(nameof(port));
@@ -38,6 +42,7 @@ namespace Core.Inventory.Application.UseCases.Product
_updateProductValidator = updateProductValidator ?? throw new ArgumentNullException(nameof(updateProductValidator));
_inventoryServiceClient = inventoryDALService ?? throw new ArgumentNullException(nameof(inventoryDALService));
_productsByListValidator = productsByListValidator ?? throw new ArgumentNullException(nameof(productsByListValidator));
_deleteProductValidator = deleteProductValidator ?? throw new ArgumentNullException(nameof(deleteProductValidator));
}
public async ValueTask ExecuteAsync(GetProductRequest command, CancellationToken cancellationToken = default)
@@ -153,7 +158,7 @@ namespace Core.Inventory.Application.UseCases.Product
TenantId = command.TenantId,
ProductName = command.ProductName,
Description = command.Description,
Status = command.Status,
ProductStatus = command.ProductStatus,
TagIds = command.TagIds
};
@@ -191,7 +196,7 @@ namespace Core.Inventory.Application.UseCases.Product
TenantId = command.TenantId,
ProductName = command.ProductName,
Description = command.Description,
Status = Enum.Parse<ProductStatus>(command.Status),
ProductStatus = Enum.Parse<ProductStatus>(command.ProductStatus),
TagIds = command.TagIds.Select(id => MongoDB.Bson.ObjectId.Parse(id)).ToList()
};
@@ -210,5 +215,33 @@ namespace Core.Inventory.Application.UseCases.Product
ApiResponseHelper.EvaluatePort(ex, _port);
}
}
public async ValueTask ExecuteAsync(DeleteProductRequest command, CancellationToken cancellationToken = default)
{
try
{
ArgumentNullException.ThrowIfNull(command);
if (!command.IsValid(_deleteProductValidator))
{
_port.ValidationErrors(command.Notifications);
return;
}
var result = await _inventoryServiceClient.DeleteProductAsync(command.Id, cancellationToken).ConfigureAwait(false);
if (!result)
{
_port.NoContentSuccess();
return;
}
_port.Success(new DeleteProductResponseAdapter() { Success = true });
}
catch (Exception ex)
{
ApiResponseHelper.EvaluatePort(ex, _port);
}
}
}
}

View File

@@ -0,0 +1,13 @@
using Core.Inventory.Application.UseCases.Product.Input;
using FluentValidation;
namespace Core.Inventory.Application.UseCases.Product.Validator
{
public class DeleteProductValidator : AbstractValidator<DeleteProductRequest>
{
public DeleteProductValidator()
{
RuleFor(i => i.Id).NotEmpty().NotNull().OverridePropertyName(x => x.Id).WithName("Product Id").WithMessage("Product Id is Obligatory.");
}
}
}

View File

@@ -129,6 +129,9 @@ namespace Core.Inventory.External.Clients
[Delete("/api/v1/Product/{productId}/tags/{tagId}")]
Task<ProductAdapter> RemoveTagFromProductAsync([FromRoute] string productId, [FromRoute] string tagId, CancellationToken cancellationToken = default);
[Delete("/api/v1/Product/{id}")]
Task<bool> DeleteProductAsync([FromRoute] string id, CancellationToken cancellationToken = default);
#endregion
}
}

View File

@@ -5,7 +5,7 @@ namespace Core.Inventory.External.Clients.Requests
public string TenantId { get; set; } = null!;
public string ProductName { get; set; } = null!;
public string Description { get; set; } = null!;
public string Status { get; set; } = null!;
public string ProductStatus { get; set; } = null!;
public List<string> TagIds { get; set; } = new List<string>();
}
}

View File

@@ -7,7 +7,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Adapters.Lib" Version="1.0.11" />
<PackageReference Include="Adapters.Lib" Version="1.0.13" />
<PackageReference Include="BuildingBlocks.Library" Version="1.0.0" />
<PackageReference Include="Refit" Version="8.0.0" />
</ItemGroup>

View File

@@ -24,6 +24,7 @@ namespace Core.Inventory.Service.API.Controllers
private readonly IComponentHandler<CreateProductRequest> createProductHandler;
private readonly IComponentHandler<UpdateProductRequest> updateProductHandler;
private readonly IComponentHandler<ChangeProductStatusRequest> changeProductStatusHandler;
private readonly IComponentHandler<DeleteProductRequest> deleteProductHandler;
private readonly IProductPort port;
/// <summary>
@@ -36,6 +37,7 @@ namespace Core.Inventory.Service.API.Controllers
IComponentHandler<CreateProductRequest> createProductHandler,
IComponentHandler<UpdateProductRequest> updateProductHandler,
IComponentHandler<ChangeProductStatusRequest> changeProductStatusHandler,
IComponentHandler<DeleteProductRequest> deleteProductHandler,
IProductPort port
)
{
@@ -45,6 +47,7 @@ namespace Core.Inventory.Service.API.Controllers
this.getAllProductsHandler = getAllProductsHandler;
this.getProductHandler = getProductHandler;
this.getAllProductsByListHandler = getAllProductsByListHandler;
this.deleteProductHandler = deleteProductHandler;
this.port = port;
}
@@ -165,6 +168,15 @@ namespace Core.Inventory.Service.API.Controllers
/// <summary>
/// Changes the status of the Product.
/// </summary>
/// <param name="request">The request containing the product ID and new ProductStatus.</param>
/// <returns>The <see cref="ProductAdapter"/> updated entity.</returns>
/// <response code="200">The Product updates.</response>
/// <response code="204">The Product not found.</response>
/// <response code="400">The Product could not be updated.</response>
/// <response code="401">The Product could not be updated.</response>
/// <response code="412">The Product could not be updated.</response>
/// <response code="422">The Product could not be updated.</response>
/// <response code="500">The service internal error.</response>
[HttpPatch]
[Route("ChangeStatus")]
[ProducesResponseType(StatusCodes.Status200OK)]
@@ -183,5 +195,38 @@ namespace Core.Inventory.Service.API.Controllers
return port.ViewModel;
}
/// <summary>
/// Deletes a Product by its MongoDB identifier.
/// </summary>
/// <param name="request">The request containing the product ID to delete.</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 Product deleted successfully.</response>
/// <response code="204">No content if the Product was not found.</response>
/// <response code="400">Bad request if the Product ID is 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>
[HttpDelete("Delete")]
[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> DeleteProductAsync([FromBody] DeleteProductRequest request, CancellationToken cancellationToken = default)
{
if (string.IsNullOrEmpty(request.Id))
{
return BadRequest("Invalid Product identifier");
}
await deleteProductHandler.ExecuteAsync(request, cancellationToken).ConfigureAwait(false);
return port.ViewModel;
}
}
}

View File

@@ -154,6 +154,10 @@ namespace Core.Inventory.Service.API.Extensions
services.AddScoped<IValidator<ChangeProductStatusRequest>, ChangeProductStatusValidator>();
services.AddScoped<IComponentHandler<ChangeProductStatusRequest>, ProductHandler>();
services.AddValidatorsFromAssemblyContaining<DeleteProductValidator>();
services.AddScoped<IValidator<DeleteProductRequest>, DeleteProductValidator>();
services.AddScoped<IComponentHandler<DeleteProductRequest>, ProductHandler>();
#endregion
return services;