From 40906bea78010460c4484ba0a7c79f2ec8f12c0d Mon Sep 17 00:00:00 2001 From: lumijiez <59575049+lumijiez@users.noreply.github.com> Date: Tue, 17 Jun 2025 15:16:02 +0300 Subject: [PATCH] Add CRUDs and validation/controllers --- .../Categories}/CreateCategoryHandler.cs | 2 +- .../Categories}/DeleteCategoryHandler.cs | 2 +- .../Categories}/Dtos/CategoryDto.cs | 0 .../Categories}/GetCategoriesHandler.cs | 2 +- .../Categories/UpdateCategoryHandler.cs | 62 +++++++++++++++ .../CreateProductVariantHandler.cs | 2 +- .../DeleteProductVariantHandler.cs | 2 +- .../Dtos/ProductVariantDto.cs | 0 .../GetProductVariantsHandler.cs | 2 +- .../UpdateProductVariantHandler.cs | 69 ++++++++++++++++ .../Products}/CreateProductHandler.cs | 2 +- .../Products}/DeleteProductHandler.cs | 2 +- .../Products/Dtos/PagedResultDto.cs | 0 .../{ => Domains}/Products/Dtos/ProductDto.cs | 0 .../Products/GetProductsHandler.cs | 2 +- .../Domains/Products/UpdateProductCommand.cs | 78 +++++++++++++++++++ .../Users/DeleteUserRoleHandler.cs | 3 +- .../{ => Domains}/Users/Dtos/RoleDto.cs | 0 .../{ => Domains}/Users/Dtos/UserDto.cs | 0 .../{ => Domains}/Users/Dtos/UserRoleDto.cs | 0 .../Users/GetUserRolesHandler.cs | 2 +- .../Users/SetUserFullNameHandler.cs | 4 +- .../Users/SetUserPhoneHandler.cs | 4 +- .../{ => Domains}/Users/SetUserRoleHandler.cs | 2 +- .../{ => Domains}/Users/SyncUserHandler.cs | 2 +- .../Imprink.Application.csproj | 2 +- .../ICurrentUserService.cs | 2 +- .../CreateCategoryCommandValidator.cs | 34 ++++++++ .../DeleteCategoryCommandValidator.cs | 14 ++++ .../UpdateCategoryCommandValidator.cs | 40 ++++++++++ .../CreateProducVariantCommandValidator.cs | 41 ++++++++++ .../DeleteProductVariantCommandValidator.cs | 14 ++++ .../GetProductVariantsQueryValidator.cs | 14 ++++ .../UpdateProductVariantCommandValidator.cs | 45 +++++++++++ .../Products/CreateProductCommandValidator.cs | 34 ++++++++ .../Products/DeleteProductCommandValidator.cs | 14 ++++ .../Products/GetProductsQueryValidator.cs | 1 + .../Products/UpdateProductCommandValidator.cs | 38 +++++++++ .../Users/SetUserFullNameCommandValidator.cs | 18 +++++ .../Users/SetUserPhoneCommandValidator.cs | 14 ++++ src/Imprink.Domain/Imprink.Domain.csproj | 4 + .../Imprink.Infrastructure.csproj | 1 + .../Services/CurrentUserService.cs | 2 +- .../Products/CategoriesController.cs | 35 ++++++++- .../Products/ProductVariantsController.cs | 32 +++++++- .../Products/ProductsController.cs | 30 +++++++ .../Controllers/Users/UsersController.cs | 2 +- src/Imprink.WebApi/Startup.cs | 5 +- 48 files changed, 653 insertions(+), 27 deletions(-) rename src/Imprink.Application/{Products/Create => Domains/Categories}/CreateCategoryHandler.cs (97%) rename src/Imprink.Application/{Products/Delete => Domains/Categories}/DeleteCategoryHandler.cs (95%) rename src/Imprink.Application/{Products => Domains/Categories}/Dtos/CategoryDto.cs (100%) rename src/Imprink.Application/{Products/Query => Domains/Categories}/GetCategoriesHandler.cs (96%) create mode 100644 src/Imprink.Application/Domains/Categories/UpdateCategoryHandler.cs rename src/Imprink.Application/{Products/Create => Domains/ProductVariants}/CreateProductVariantHandler.cs (97%) rename src/Imprink.Application/{Products/Delete => Domains/ProductVariants}/DeleteProductVariantHandler.cs (95%) rename src/Imprink.Application/{Products => Domains/ProductVariants}/Dtos/ProductVariantDto.cs (100%) rename src/Imprink.Application/{Products/Query => Domains/ProductVariants}/GetProductVariantsHandler.cs (97%) create mode 100644 src/Imprink.Application/Domains/ProductVariants/UpdateProductVariantHandler.cs rename src/Imprink.Application/{Products/Create => Domains/Products}/CreateProductHandler.cs (98%) rename src/Imprink.Application/{Products/Delete => Domains/Products}/DeleteProductHandler.cs (95%) rename src/Imprink.Application/{ => Domains}/Products/Dtos/PagedResultDto.cs (100%) rename src/Imprink.Application/{ => Domains}/Products/Dtos/ProductDto.cs (100%) rename src/Imprink.Application/{ => Domains}/Products/GetProductsHandler.cs (97%) create mode 100644 src/Imprink.Application/Domains/Products/UpdateProductCommand.cs rename src/Imprink.Application/{ => Domains}/Users/DeleteUserRoleHandler.cs (95%) rename src/Imprink.Application/{ => Domains}/Users/Dtos/RoleDto.cs (100%) rename src/Imprink.Application/{ => Domains}/Users/Dtos/UserDto.cs (100%) rename src/Imprink.Application/{ => Domains}/Users/Dtos/UserRoleDto.cs (100%) rename src/Imprink.Application/{ => Domains}/Users/GetUserRolesHandler.cs (94%) rename src/Imprink.Application/{ => Domains}/Users/SetUserFullNameHandler.cs (95%) rename src/Imprink.Application/{ => Domains}/Users/SetUserPhoneHandler.cs (95%) rename src/Imprink.Application/{ => Domains}/Users/SetUserRoleHandler.cs (96%) rename src/Imprink.Application/{ => Domains}/Users/SyncUserHandler.cs (96%) rename src/Imprink.Application/{Service => Services}/ICurrentUserService.cs (64%) create mode 100644 src/Imprink.Application/Validation/Categories/CreateCategoryCommandValidator.cs create mode 100644 src/Imprink.Application/Validation/Categories/DeleteCategoryCommandValidator.cs create mode 100644 src/Imprink.Application/Validation/Categories/UpdateCategoryCommandValidator.cs create mode 100644 src/Imprink.Application/Validation/ProductVariants/CreateProducVariantCommandValidator.cs create mode 100644 src/Imprink.Application/Validation/ProductVariants/DeleteProductVariantCommandValidator.cs create mode 100644 src/Imprink.Application/Validation/ProductVariants/GetProductVariantsQueryValidator.cs create mode 100644 src/Imprink.Application/Validation/ProductVariants/UpdateProductVariantCommandValidator.cs create mode 100644 src/Imprink.Application/Validation/Products/CreateProductCommandValidator.cs create mode 100644 src/Imprink.Application/Validation/Products/DeleteProductCommandValidator.cs create mode 100644 src/Imprink.Application/Validation/Products/UpdateProductCommandValidator.cs create mode 100644 src/Imprink.Application/Validation/Users/SetUserFullNameCommandValidator.cs create mode 100644 src/Imprink.Application/Validation/Users/SetUserPhoneCommandValidator.cs diff --git a/src/Imprink.Application/Products/Create/CreateCategoryHandler.cs b/src/Imprink.Application/Domains/Categories/CreateCategoryHandler.cs similarity index 97% rename from src/Imprink.Application/Products/Create/CreateCategoryHandler.cs rename to src/Imprink.Application/Domains/Categories/CreateCategoryHandler.cs index 6b3cda4..2b67a75 100644 --- a/src/Imprink.Application/Products/Create/CreateCategoryHandler.cs +++ b/src/Imprink.Application/Domains/Categories/CreateCategoryHandler.cs @@ -2,7 +2,7 @@ using Imprink.Application.Products.Dtos; using Imprink.Domain.Entities.Product; using MediatR; -namespace Imprink.Application.Products.Create; +namespace Imprink.Application.Domains.Categories; public class CreateCategoryCommand : IRequest { diff --git a/src/Imprink.Application/Products/Delete/DeleteCategoryHandler.cs b/src/Imprink.Application/Domains/Categories/DeleteCategoryHandler.cs similarity index 95% rename from src/Imprink.Application/Products/Delete/DeleteCategoryHandler.cs rename to src/Imprink.Application/Domains/Categories/DeleteCategoryHandler.cs index fe6dfc2..b3d5850 100644 --- a/src/Imprink.Application/Products/Delete/DeleteCategoryHandler.cs +++ b/src/Imprink.Application/Domains/Categories/DeleteCategoryHandler.cs @@ -1,6 +1,6 @@ using MediatR; -namespace Imprink.Application.Products.Delete; +namespace Imprink.Application.Domains.Categories; public class DeleteCategoryCommand : IRequest { diff --git a/src/Imprink.Application/Products/Dtos/CategoryDto.cs b/src/Imprink.Application/Domains/Categories/Dtos/CategoryDto.cs similarity index 100% rename from src/Imprink.Application/Products/Dtos/CategoryDto.cs rename to src/Imprink.Application/Domains/Categories/Dtos/CategoryDto.cs diff --git a/src/Imprink.Application/Products/Query/GetCategoriesHandler.cs b/src/Imprink.Application/Domains/Categories/GetCategoriesHandler.cs similarity index 96% rename from src/Imprink.Application/Products/Query/GetCategoriesHandler.cs rename to src/Imprink.Application/Domains/Categories/GetCategoriesHandler.cs index 0baf581..a0b13f1 100644 --- a/src/Imprink.Application/Products/Query/GetCategoriesHandler.cs +++ b/src/Imprink.Application/Domains/Categories/GetCategoriesHandler.cs @@ -2,7 +2,7 @@ using Imprink.Application.Products.Dtos; using Imprink.Domain.Entities.Product; using MediatR; -namespace Imprink.Application.Products.Query; +namespace Imprink.Application.Domains.Categories; public class GetCategoriesQuery : IRequest> { diff --git a/src/Imprink.Application/Domains/Categories/UpdateCategoryHandler.cs b/src/Imprink.Application/Domains/Categories/UpdateCategoryHandler.cs new file mode 100644 index 0000000..a10eb1f --- /dev/null +++ b/src/Imprink.Application/Domains/Categories/UpdateCategoryHandler.cs @@ -0,0 +1,62 @@ +using Imprink.Application.Exceptions; +using Imprink.Application.Products.Dtos; +using MediatR; + +namespace Imprink.Application.Domains.Categories; + +public class UpdateCategoryCommand : IRequest +{ + public Guid Id { get; set; } + public string Name { get; set; } = null!; + public string Description { get; set; } = null!; + public string? ImageUrl { get; set; } + public int SortOrder { get; set; } + public bool IsActive { get; set; } + public Guid? ParentCategoryId { get; set; } +} + +public class UpdateCategoryHandler(IUnitOfWork unitOfWork) : IRequestHandler +{ + public async Task Handle(UpdateCategoryCommand request, CancellationToken cancellationToken) + { + await unitOfWork.BeginTransactionAsync(cancellationToken); + + try + { + var existingCategory = await unitOfWork.CategoryRepository.GetByIdAsync(request.Id, cancellationToken); + + if (existingCategory == null) + { + throw new NotFoundException($"Category with ID {request.Id} not found."); + } + + existingCategory.Name = request.Name; + existingCategory.Description = request.Description; + existingCategory.ImageUrl = request.ImageUrl; + existingCategory.SortOrder = request.SortOrder; + existingCategory.IsActive = request.IsActive; + existingCategory.ParentCategoryId = request.ParentCategoryId; + + var updatedCategory = await unitOfWork.CategoryRepository.UpdateAsync(existingCategory, cancellationToken); + await unitOfWork.CommitTransactionAsync(cancellationToken); + + return new CategoryDto + { + Id = updatedCategory.Id, + Name = updatedCategory.Name, + Description = updatedCategory.Description, + ImageUrl = updatedCategory.ImageUrl, + SortOrder = updatedCategory.SortOrder, + IsActive = updatedCategory.IsActive, + ParentCategoryId = updatedCategory.ParentCategoryId, + CreatedAt = updatedCategory.CreatedAt, + ModifiedAt = updatedCategory.ModifiedAt + }; + } + catch + { + await unitOfWork.RollbackTransactionAsync(cancellationToken); + throw; + } + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Products/Create/CreateProductVariantHandler.cs b/src/Imprink.Application/Domains/ProductVariants/CreateProductVariantHandler.cs similarity index 97% rename from src/Imprink.Application/Products/Create/CreateProductVariantHandler.cs rename to src/Imprink.Application/Domains/ProductVariants/CreateProductVariantHandler.cs index c01eacd..9a6aef9 100644 --- a/src/Imprink.Application/Products/Create/CreateProductVariantHandler.cs +++ b/src/Imprink.Application/Domains/ProductVariants/CreateProductVariantHandler.cs @@ -2,7 +2,7 @@ using Imprink.Application.Products.Dtos; using Imprink.Domain.Entities.Product; using MediatR; -namespace Imprink.Application.Products.Create; +namespace Imprink.Application.Domains.ProductVariants; public class CreateProductVariantCommand : IRequest { diff --git a/src/Imprink.Application/Products/Delete/DeleteProductVariantHandler.cs b/src/Imprink.Application/Domains/ProductVariants/DeleteProductVariantHandler.cs similarity index 95% rename from src/Imprink.Application/Products/Delete/DeleteProductVariantHandler.cs rename to src/Imprink.Application/Domains/ProductVariants/DeleteProductVariantHandler.cs index 410fb3a..10e257e 100644 --- a/src/Imprink.Application/Products/Delete/DeleteProductVariantHandler.cs +++ b/src/Imprink.Application/Domains/ProductVariants/DeleteProductVariantHandler.cs @@ -1,6 +1,6 @@ using MediatR; -namespace Imprink.Application.Products.Delete; +namespace Imprink.Application.Domains.ProductVariants; public class DeleteProductVariantCommand : IRequest { diff --git a/src/Imprink.Application/Products/Dtos/ProductVariantDto.cs b/src/Imprink.Application/Domains/ProductVariants/Dtos/ProductVariantDto.cs similarity index 100% rename from src/Imprink.Application/Products/Dtos/ProductVariantDto.cs rename to src/Imprink.Application/Domains/ProductVariants/Dtos/ProductVariantDto.cs diff --git a/src/Imprink.Application/Products/Query/GetProductVariantsHandler.cs b/src/Imprink.Application/Domains/ProductVariants/GetProductVariantsHandler.cs similarity index 97% rename from src/Imprink.Application/Products/Query/GetProductVariantsHandler.cs rename to src/Imprink.Application/Domains/ProductVariants/GetProductVariantsHandler.cs index adfa831..609036b 100644 --- a/src/Imprink.Application/Products/Query/GetProductVariantsHandler.cs +++ b/src/Imprink.Application/Domains/ProductVariants/GetProductVariantsHandler.cs @@ -2,7 +2,7 @@ using Imprink.Application.Products.Dtos; using Imprink.Domain.Entities.Product; using MediatR; -namespace Imprink.Application.Products.Query; +namespace Imprink.Application.Domains.ProductVariants; public class GetProductVariantsQuery : IRequest> { diff --git a/src/Imprink.Application/Domains/ProductVariants/UpdateProductVariantHandler.cs b/src/Imprink.Application/Domains/ProductVariants/UpdateProductVariantHandler.cs new file mode 100644 index 0000000..b7574b1 --- /dev/null +++ b/src/Imprink.Application/Domains/ProductVariants/UpdateProductVariantHandler.cs @@ -0,0 +1,69 @@ +using Imprink.Application.Exceptions; +using Imprink.Application.Products.Dtos; +using MediatR; + +namespace Imprink.Application.Domains.ProductVariants; + +public class UpdateProductVariantCommand : IRequest +{ + public Guid Id { get; set; } + public Guid ProductId { get; set; } + public string Size { get; set; } = null!; + public string? Color { get; set; } + public decimal Price { get; set; } + public string? ImageUrl { get; set; } + public string Sku { get; set; } = null!; + public int StockQuantity { get; set; } + public bool IsActive { get; set; } +} + +public class UpdateProductVariantHandler(IUnitOfWork unitOfWork) + : IRequestHandler +{ + public async Task Handle(UpdateProductVariantCommand request, CancellationToken cancellationToken) + { + await unitOfWork.BeginTransactionAsync(cancellationToken); + + try + { + var existingVariant = await unitOfWork.ProductVariantRepository.GetByIdAsync(request.Id, cancellationToken); + + if (existingVariant == null) + { + throw new NotFoundException($"Product variant with ID {request.Id} not found."); + } + + existingVariant.ProductId = request.ProductId; + existingVariant.Size = request.Size; + existingVariant.Color = request.Color; + existingVariant.Price = request.Price; + existingVariant.ImageUrl = request.ImageUrl; + existingVariant.Sku = request.Sku; + existingVariant.StockQuantity = request.StockQuantity; + existingVariant.IsActive = request.IsActive; + + var updatedVariant = await unitOfWork.ProductVariantRepository.UpdateAsync(existingVariant, cancellationToken); + await unitOfWork.CommitTransactionAsync(cancellationToken); + + return new ProductVariantDto + { + Id = updatedVariant.Id, + ProductId = updatedVariant.ProductId, + Size = updatedVariant.Size, + Color = updatedVariant.Color, + Price = updatedVariant.Price, + ImageUrl = updatedVariant.ImageUrl, + Sku = updatedVariant.Sku, + StockQuantity = updatedVariant.StockQuantity, + IsActive = updatedVariant.IsActive, + CreatedAt = updatedVariant.CreatedAt, + ModifiedAt = updatedVariant.ModifiedAt + }; + } + catch + { + await unitOfWork.RollbackTransactionAsync(cancellationToken); + throw; + } + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Products/Create/CreateProductHandler.cs b/src/Imprink.Application/Domains/Products/CreateProductHandler.cs similarity index 98% rename from src/Imprink.Application/Products/Create/CreateProductHandler.cs rename to src/Imprink.Application/Domains/Products/CreateProductHandler.cs index 8cdce4c..a9a0df7 100644 --- a/src/Imprink.Application/Products/Create/CreateProductHandler.cs +++ b/src/Imprink.Application/Domains/Products/CreateProductHandler.cs @@ -2,7 +2,7 @@ using Imprink.Application.Products.Dtos; using Imprink.Domain.Entities.Product; using MediatR; -namespace Imprink.Application.Products.Create; +namespace Imprink.Application.Domains.Products; public class CreateProductCommand : IRequest { diff --git a/src/Imprink.Application/Products/Delete/DeleteProductHandler.cs b/src/Imprink.Application/Domains/Products/DeleteProductHandler.cs similarity index 95% rename from src/Imprink.Application/Products/Delete/DeleteProductHandler.cs rename to src/Imprink.Application/Domains/Products/DeleteProductHandler.cs index 70edc5a..c81d3d8 100644 --- a/src/Imprink.Application/Products/Delete/DeleteProductHandler.cs +++ b/src/Imprink.Application/Domains/Products/DeleteProductHandler.cs @@ -1,6 +1,6 @@ using MediatR; -namespace Imprink.Application.Products.Delete; +namespace Imprink.Application.Domains.Products; public class DeleteProductCommand : IRequest { diff --git a/src/Imprink.Application/Products/Dtos/PagedResultDto.cs b/src/Imprink.Application/Domains/Products/Dtos/PagedResultDto.cs similarity index 100% rename from src/Imprink.Application/Products/Dtos/PagedResultDto.cs rename to src/Imprink.Application/Domains/Products/Dtos/PagedResultDto.cs diff --git a/src/Imprink.Application/Products/Dtos/ProductDto.cs b/src/Imprink.Application/Domains/Products/Dtos/ProductDto.cs similarity index 100% rename from src/Imprink.Application/Products/Dtos/ProductDto.cs rename to src/Imprink.Application/Domains/Products/Dtos/ProductDto.cs diff --git a/src/Imprink.Application/Products/GetProductsHandler.cs b/src/Imprink.Application/Domains/Products/GetProductsHandler.cs similarity index 97% rename from src/Imprink.Application/Products/GetProductsHandler.cs rename to src/Imprink.Application/Domains/Products/GetProductsHandler.cs index afd64cc..615553d 100644 --- a/src/Imprink.Application/Products/GetProductsHandler.cs +++ b/src/Imprink.Application/Domains/Products/GetProductsHandler.cs @@ -2,7 +2,7 @@ using Imprink.Application.Products.Dtos; using Imprink.Domain.Models; using MediatR; -namespace Imprink.Application.Products; +namespace Imprink.Application.Domains.Products; public class GetProductsQuery : IRequest> { diff --git a/src/Imprink.Application/Domains/Products/UpdateProductCommand.cs b/src/Imprink.Application/Domains/Products/UpdateProductCommand.cs new file mode 100644 index 0000000..3aadffa --- /dev/null +++ b/src/Imprink.Application/Domains/Products/UpdateProductCommand.cs @@ -0,0 +1,78 @@ +using Imprink.Application.Exceptions; +using Imprink.Application.Products.Dtos; +using MediatR; + +namespace Imprink.Application.Domains.Products; + +public class UpdateProductCommand : IRequest +{ + public Guid Id { get; set; } + public string Name { get; set; } = null!; + public string? Description { get; set; } + public decimal BasePrice { get; set; } + public bool IsCustomizable { get; set; } + public bool IsActive { get; set; } + public string? ImageUrl { get; set; } + public Guid? CategoryId { get; set; } +} + +public class UpdateProductHandler(IUnitOfWork unitOfWork) : IRequestHandler +{ + public async Task Handle(UpdateProductCommand request, CancellationToken cancellationToken) + { + await unitOfWork.BeginTransactionAsync(cancellationToken); + + try + { + var existingProduct = await unitOfWork.ProductRepository.GetByIdAsync(request.Id, cancellationToken); + + if (existingProduct == null) + throw new NotFoundException($"Product with ID {request.Id} not found."); + + existingProduct.Name = request.Name; + existingProduct.Description = request.Description; + existingProduct.BasePrice = request.BasePrice; + existingProduct.IsCustomizable = request.IsCustomizable; + existingProduct.IsActive = request.IsActive; + existingProduct.ImageUrl = request.ImageUrl; + existingProduct.CategoryId = request.CategoryId; + + var updatedProduct = await unitOfWork.ProductRepository.UpdateAsync(existingProduct, cancellationToken); + + var categoryDto = new CategoryDto + { + Id = updatedProduct.Category.Id, + Name = updatedProduct.Category.Name, + Description = updatedProduct.Category.Description, + ImageUrl = updatedProduct.Category.ImageUrl, + SortOrder = updatedProduct.Category.SortOrder, + IsActive = updatedProduct.Category.IsActive, + ParentCategoryId = updatedProduct.Category.ParentCategoryId, + CreatedAt = updatedProduct.Category.CreatedAt, + ModifiedAt = updatedProduct.Category.ModifiedAt + }; + + await unitOfWork.CommitTransactionAsync(cancellationToken); + + return new ProductDto + { + Id = updatedProduct.Id, + Name = updatedProduct.Name, + Description = updatedProduct.Description, + BasePrice = updatedProduct.BasePrice, + IsCustomizable = updatedProduct.IsCustomizable, + IsActive = updatedProduct.IsActive, + ImageUrl = updatedProduct.ImageUrl, + CategoryId = updatedProduct.CategoryId, + Category = categoryDto, + CreatedAt = updatedProduct.CreatedAt, + ModifiedAt = updatedProduct.ModifiedAt + }; + } + catch + { + await unitOfWork.RollbackTransactionAsync(cancellationToken); + throw; + } + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Users/DeleteUserRoleHandler.cs b/src/Imprink.Application/Domains/Users/DeleteUserRoleHandler.cs similarity index 95% rename from src/Imprink.Application/Users/DeleteUserRoleHandler.cs rename to src/Imprink.Application/Domains/Users/DeleteUserRoleHandler.cs index a5424ee..28bed6d 100644 --- a/src/Imprink.Application/Users/DeleteUserRoleHandler.cs +++ b/src/Imprink.Application/Domains/Users/DeleteUserRoleHandler.cs @@ -1,9 +1,8 @@ using Imprink.Application.Exceptions; using Imprink.Application.Users.Dtos; -using Imprink.Domain.Entities.Users; using MediatR; -namespace Imprink.Application.Users; +namespace Imprink.Application.Domains.Users; public record DeleteUserRoleCommand(string Sub, Guid RoleId) : IRequest; diff --git a/src/Imprink.Application/Users/Dtos/RoleDto.cs b/src/Imprink.Application/Domains/Users/Dtos/RoleDto.cs similarity index 100% rename from src/Imprink.Application/Users/Dtos/RoleDto.cs rename to src/Imprink.Application/Domains/Users/Dtos/RoleDto.cs diff --git a/src/Imprink.Application/Users/Dtos/UserDto.cs b/src/Imprink.Application/Domains/Users/Dtos/UserDto.cs similarity index 100% rename from src/Imprink.Application/Users/Dtos/UserDto.cs rename to src/Imprink.Application/Domains/Users/Dtos/UserDto.cs diff --git a/src/Imprink.Application/Users/Dtos/UserRoleDto.cs b/src/Imprink.Application/Domains/Users/Dtos/UserRoleDto.cs similarity index 100% rename from src/Imprink.Application/Users/Dtos/UserRoleDto.cs rename to src/Imprink.Application/Domains/Users/Dtos/UserRoleDto.cs diff --git a/src/Imprink.Application/Users/GetUserRolesHandler.cs b/src/Imprink.Application/Domains/Users/GetUserRolesHandler.cs similarity index 94% rename from src/Imprink.Application/Users/GetUserRolesHandler.cs rename to src/Imprink.Application/Domains/Users/GetUserRolesHandler.cs index dca1ed0..acd95a2 100644 --- a/src/Imprink.Application/Users/GetUserRolesHandler.cs +++ b/src/Imprink.Application/Domains/Users/GetUserRolesHandler.cs @@ -2,7 +2,7 @@ using Imprink.Application.Exceptions; using Imprink.Application.Users.Dtos; using MediatR; -namespace Imprink.Application.Users; +namespace Imprink.Application.Domains.Users; public record GetUserRolesCommand(string Sub) : IRequest>; diff --git a/src/Imprink.Application/Users/SetUserFullNameHandler.cs b/src/Imprink.Application/Domains/Users/SetUserFullNameHandler.cs similarity index 95% rename from src/Imprink.Application/Users/SetUserFullNameHandler.cs rename to src/Imprink.Application/Domains/Users/SetUserFullNameHandler.cs index 68efb8a..b8b4ce1 100644 --- a/src/Imprink.Application/Users/SetUserFullNameHandler.cs +++ b/src/Imprink.Application/Domains/Users/SetUserFullNameHandler.cs @@ -1,9 +1,9 @@ using Imprink.Application.Exceptions; -using Imprink.Application.Service; +using Imprink.Application.Services; using Imprink.Application.Users.Dtos; using MediatR; -namespace Imprink.Application.Users; +namespace Imprink.Application.Domains.Users; public record SetUserFullNameCommand(string FirstName, string LastName) : IRequest; diff --git a/src/Imprink.Application/Users/SetUserPhoneHandler.cs b/src/Imprink.Application/Domains/Users/SetUserPhoneHandler.cs similarity index 95% rename from src/Imprink.Application/Users/SetUserPhoneHandler.cs rename to src/Imprink.Application/Domains/Users/SetUserPhoneHandler.cs index 47c064b..b7b5289 100644 --- a/src/Imprink.Application/Users/SetUserPhoneHandler.cs +++ b/src/Imprink.Application/Domains/Users/SetUserPhoneHandler.cs @@ -1,9 +1,9 @@ using Imprink.Application.Exceptions; -using Imprink.Application.Service; +using Imprink.Application.Services; using Imprink.Application.Users.Dtos; using MediatR; -namespace Imprink.Application.Users; +namespace Imprink.Application.Domains.Users; public record SetUserPhoneCommand(string PhoneNumber) : IRequest; diff --git a/src/Imprink.Application/Users/SetUserRoleHandler.cs b/src/Imprink.Application/Domains/Users/SetUserRoleHandler.cs similarity index 96% rename from src/Imprink.Application/Users/SetUserRoleHandler.cs rename to src/Imprink.Application/Domains/Users/SetUserRoleHandler.cs index f077e99..426eb8b 100644 --- a/src/Imprink.Application/Users/SetUserRoleHandler.cs +++ b/src/Imprink.Application/Domains/Users/SetUserRoleHandler.cs @@ -3,7 +3,7 @@ using Imprink.Application.Users.Dtos; using Imprink.Domain.Entities.Users; using MediatR; -namespace Imprink.Application.Users; +namespace Imprink.Application.Domains.Users; public record SetUserRoleCommand(string Sub, Guid RoleId) : IRequest; diff --git a/src/Imprink.Application/Users/SyncUserHandler.cs b/src/Imprink.Application/Domains/Users/SyncUserHandler.cs similarity index 96% rename from src/Imprink.Application/Users/SyncUserHandler.cs rename to src/Imprink.Application/Domains/Users/SyncUserHandler.cs index edcf817..503521a 100644 --- a/src/Imprink.Application/Users/SyncUserHandler.cs +++ b/src/Imprink.Application/Domains/Users/SyncUserHandler.cs @@ -2,7 +2,7 @@ using Imprink.Application.Users.Dtos; using Imprink.Domain.Models; using MediatR; -namespace Imprink.Application.Users; +namespace Imprink.Application.Domains.Users; public record SyncUserCommand(Auth0User User) : IRequest; diff --git a/src/Imprink.Application/Imprink.Application.csproj b/src/Imprink.Application/Imprink.Application.csproj index b274257..5a13bfc 100644 --- a/src/Imprink.Application/Imprink.Application.csproj +++ b/src/Imprink.Application/Imprink.Application.csproj @@ -20,7 +20,7 @@ - + diff --git a/src/Imprink.Application/Service/ICurrentUserService.cs b/src/Imprink.Application/Services/ICurrentUserService.cs similarity index 64% rename from src/Imprink.Application/Service/ICurrentUserService.cs rename to src/Imprink.Application/Services/ICurrentUserService.cs index bd6ad1e..83bbe41 100644 --- a/src/Imprink.Application/Service/ICurrentUserService.cs +++ b/src/Imprink.Application/Services/ICurrentUserService.cs @@ -1,4 +1,4 @@ -namespace Imprink.Application.Service; +namespace Imprink.Application.Services; public interface ICurrentUserService { diff --git a/src/Imprink.Application/Validation/Categories/CreateCategoryCommandValidator.cs b/src/Imprink.Application/Validation/Categories/CreateCategoryCommandValidator.cs new file mode 100644 index 0000000..6f20535 --- /dev/null +++ b/src/Imprink.Application/Validation/Categories/CreateCategoryCommandValidator.cs @@ -0,0 +1,34 @@ +using FluentValidation; +using Imprink.Application.Domains.Categories; + +namespace Imprink.Application.Validation.Categories; + +public class CreateCategoryCommandValidator : AbstractValidator +{ + public CreateCategoryCommandValidator() + { + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Name is required.") + .Length(1, 100).WithMessage("Name must be between 1 and 100 characters."); + + RuleFor(x => x.Description) + .NotEmpty().WithMessage("Description is required.") + .Length(1, 500).WithMessage("Description must be between 1 and 500 characters."); + + RuleFor(x => x.ImageUrl) + .Must(BeValidUrl).When(x => !string.IsNullOrWhiteSpace(x.ImageUrl)) + .WithMessage("ImageUrl must be a valid URL."); + + RuleFor(x => x.SortOrder) + .GreaterThanOrEqualTo(0).WithMessage("SortOrder cannot be negative."); + + RuleFor(x => x.ParentCategoryId) + .NotEqual(Guid.Empty).When(x => x.ParentCategoryId.HasValue) + .WithMessage("ParentCategoryId must be a valid GUID."); + } + + private static bool BeValidUrl(string? url) + { + return Uri.TryCreate(url, UriKind.Absolute, out _); + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Validation/Categories/DeleteCategoryCommandValidator.cs b/src/Imprink.Application/Validation/Categories/DeleteCategoryCommandValidator.cs new file mode 100644 index 0000000..d1f9cb3 --- /dev/null +++ b/src/Imprink.Application/Validation/Categories/DeleteCategoryCommandValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using Imprink.Application.Domains.Categories; + +namespace Imprink.Application.Validation.Categories; + +public class DeleteCategoryCommandValidator : AbstractValidator +{ + public DeleteCategoryCommandValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .NotEqual(Guid.Empty).WithMessage("Id must be a valid GUID."); + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Validation/Categories/UpdateCategoryCommandValidator.cs b/src/Imprink.Application/Validation/Categories/UpdateCategoryCommandValidator.cs new file mode 100644 index 0000000..7e493bf --- /dev/null +++ b/src/Imprink.Application/Validation/Categories/UpdateCategoryCommandValidator.cs @@ -0,0 +1,40 @@ +using FluentValidation; +using Imprink.Application.Domains.Categories; + +namespace Imprink.Application.Validation.Categories; + +public class UpdateCategoryCommandValidator : AbstractValidator +{ + public UpdateCategoryCommandValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .NotEqual(Guid.Empty).WithMessage("Id must be a valid GUID."); + + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Name is required.") + .Length(1, 100).WithMessage("Name must be between 1 and 100 characters."); + + RuleFor(x => x.Description) + .NotEmpty().WithMessage("Description is required.") + .Length(1, 500).WithMessage("Description must be between 1 and 500 characters."); + + RuleFor(x => x.ImageUrl) + .Must(BeValidUrl).When(x => !string.IsNullOrWhiteSpace(x.ImageUrl)) + .WithMessage("ImageUrl must be a valid URL."); + + RuleFor(x => x.SortOrder) + .GreaterThanOrEqualTo(0).WithMessage("SortOrder cannot be negative."); + + RuleFor(x => x.ParentCategoryId) + .NotEqual(Guid.Empty).When(x => x.ParentCategoryId.HasValue) + .WithMessage("ParentCategoryId must be a valid GUID.") + .Must((command, parentId) => parentId != command.Id).When(x => x.ParentCategoryId.HasValue) + .WithMessage("Category cannot be its own parent."); + } + + private static bool BeValidUrl(string? url) + { + return Uri.TryCreate(url, UriKind.Absolute, out _); + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Validation/ProductVariants/CreateProducVariantCommandValidator.cs b/src/Imprink.Application/Validation/ProductVariants/CreateProducVariantCommandValidator.cs new file mode 100644 index 0000000..c3d3957 --- /dev/null +++ b/src/Imprink.Application/Validation/ProductVariants/CreateProducVariantCommandValidator.cs @@ -0,0 +1,41 @@ +using FluentValidation; +using Imprink.Application.Domains.ProductVariants; + +namespace Imprink.Application.Validation.ProductVariants; + +public class CreateProductVariantCommandValidator : AbstractValidator +{ + public CreateProductVariantCommandValidator() + { + RuleFor(x => x.ProductId) + .NotEmpty().WithMessage("ProductId is required.") + .NotEqual(Guid.Empty).WithMessage("ProductId must be a valid GUID."); + + RuleFor(x => x.Size) + .NotEmpty().WithMessage("Size is required.") + .Length(1, 50).WithMessage("Size must be between 1 and 50 characters."); + + RuleFor(x => x.Color) + .Length(1, 50).WithMessage("Color must be between 1 and 50 characters.") + .When(x => !string.IsNullOrWhiteSpace(x.Color)); + + RuleFor(x => x.Price) + .GreaterThan(0).WithMessage("Price must be greater than 0."); + + RuleFor(x => x.ImageUrl) + .Must(BeValidUrl).When(x => !string.IsNullOrWhiteSpace(x.ImageUrl)) + .WithMessage("ImageUrl must be a valid URL."); + + RuleFor(x => x.Sku) + .NotEmpty().WithMessage("SKU is required.") + .Length(1, 50).WithMessage("SKU must be between 1 and 50 characters."); + + RuleFor(x => x.StockQuantity) + .GreaterThanOrEqualTo(0).WithMessage("StockQuantity cannot be negative."); + } + + private static bool BeValidUrl(string? url) + { + return Uri.TryCreate(url, UriKind.Absolute, out _); + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Validation/ProductVariants/DeleteProductVariantCommandValidator.cs b/src/Imprink.Application/Validation/ProductVariants/DeleteProductVariantCommandValidator.cs new file mode 100644 index 0000000..acda88f --- /dev/null +++ b/src/Imprink.Application/Validation/ProductVariants/DeleteProductVariantCommandValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using Imprink.Application.Domains.ProductVariants; + +namespace Imprink.Application.Validation.ProductVariants; + +public class DeleteProductVariantCommandValidator : AbstractValidator +{ + public DeleteProductVariantCommandValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .NotEqual(Guid.Empty).WithMessage("Id must be a valid GUID."); + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Validation/ProductVariants/GetProductVariantsQueryValidator.cs b/src/Imprink.Application/Validation/ProductVariants/GetProductVariantsQueryValidator.cs new file mode 100644 index 0000000..2ad4d09 --- /dev/null +++ b/src/Imprink.Application/Validation/ProductVariants/GetProductVariantsQueryValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using Imprink.Application.Domains.ProductVariants; + +namespace Imprink.Application.Validation.ProductVariants; + +public class GetProductVariantsQueryValidator : AbstractValidator +{ + public GetProductVariantsQueryValidator() + { + RuleFor(x => x.ProductId) + .NotEqual(Guid.Empty).When(x => x.ProductId.HasValue) + .WithMessage("ProductId must be a valid GUID when provided."); + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Validation/ProductVariants/UpdateProductVariantCommandValidator.cs b/src/Imprink.Application/Validation/ProductVariants/UpdateProductVariantCommandValidator.cs new file mode 100644 index 0000000..dd9fe23 --- /dev/null +++ b/src/Imprink.Application/Validation/ProductVariants/UpdateProductVariantCommandValidator.cs @@ -0,0 +1,45 @@ +using FluentValidation; +using Imprink.Application.Domains.ProductVariants; + +namespace Imprink.Application.Validation.ProductVariants; + +public class UpdateProductVariantCommandValidator : AbstractValidator +{ + public UpdateProductVariantCommandValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .NotEqual(Guid.Empty).WithMessage("Id must be a valid GUID."); + + RuleFor(x => x.ProductId) + .NotEmpty().WithMessage("ProductId is required.") + .NotEqual(Guid.Empty).WithMessage("ProductId must be a valid GUID."); + + RuleFor(x => x.Size) + .NotEmpty().WithMessage("Size is required.") + .Length(1, 50).WithMessage("Size must be between 1 and 50 characters."); + + RuleFor(x => x.Color) + .Length(1, 50).WithMessage("Color must be between 1 and 50 characters.") + .When(x => !string.IsNullOrWhiteSpace(x.Color)); + + RuleFor(x => x.Price) + .GreaterThan(0).WithMessage("Price must be greater than 0."); + + RuleFor(x => x.ImageUrl) + .Must(BeValidUrl).When(x => !string.IsNullOrWhiteSpace(x.ImageUrl)) + .WithMessage("ImageUrl must be a valid URL."); + + RuleFor(x => x.Sku) + .NotEmpty().WithMessage("SKU is required.") + .Length(1, 50).WithMessage("SKU must be between 1 and 50 characters."); + + RuleFor(x => x.StockQuantity) + .GreaterThanOrEqualTo(0).WithMessage("StockQuantity cannot be negative."); + } + + private static bool BeValidUrl(string? url) + { + return Uri.TryCreate(url, UriKind.Absolute, out _); + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Validation/Products/CreateProductCommandValidator.cs b/src/Imprink.Application/Validation/Products/CreateProductCommandValidator.cs new file mode 100644 index 0000000..60da2bd --- /dev/null +++ b/src/Imprink.Application/Validation/Products/CreateProductCommandValidator.cs @@ -0,0 +1,34 @@ +using FluentValidation; +using Imprink.Application.Domains.Products; + +namespace Imprink.Application.Validation.Products; + +public class CreateProductCommandValidator : AbstractValidator +{ + public CreateProductCommandValidator() + { + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Name is required.") + .Length(1, 100).WithMessage("Name must be between 1 and 100 characters."); + + RuleFor(x => x.Description) + .Length(1, 1000).WithMessage("Description must be between 1 and 1000 characters.") + .When(x => !string.IsNullOrWhiteSpace(x.Description)); + + RuleFor(x => x.BasePrice) + .GreaterThan(0).WithMessage("BasePrice must be greater than 0."); + + RuleFor(x => x.ImageUrl) + .Must(BeValidUrl).When(x => !string.IsNullOrWhiteSpace(x.ImageUrl)) + .WithMessage("ImageUrl must be a valid URL."); + + RuleFor(x => x.CategoryId) + .NotEqual(Guid.Empty).When(x => x.CategoryId.HasValue) + .WithMessage("CategoryId must be a valid GUID."); + } + + private static bool BeValidUrl(string? url) + { + return Uri.TryCreate(url, UriKind.Absolute, out _); + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Validation/Products/DeleteProductCommandValidator.cs b/src/Imprink.Application/Validation/Products/DeleteProductCommandValidator.cs new file mode 100644 index 0000000..5cb3cea --- /dev/null +++ b/src/Imprink.Application/Validation/Products/DeleteProductCommandValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using Imprink.Application.Domains.Products; + +namespace Imprink.Application.Validation.Products; + +public class DeleteProductCommandValidator : AbstractValidator +{ + public DeleteProductCommandValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .NotEqual(Guid.Empty).WithMessage("Id must be a valid GUID."); + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Validation/Products/GetProductsQueryValidator.cs b/src/Imprink.Application/Validation/Products/GetProductsQueryValidator.cs index b888657..21a6b37 100644 --- a/src/Imprink.Application/Validation/Products/GetProductsQueryValidator.cs +++ b/src/Imprink.Application/Validation/Products/GetProductsQueryValidator.cs @@ -1,4 +1,5 @@ using FluentValidation; +using Imprink.Application.Domains.Products; using Imprink.Application.Products; using Imprink.Application.Validation.Models; diff --git a/src/Imprink.Application/Validation/Products/UpdateProductCommandValidator.cs b/src/Imprink.Application/Validation/Products/UpdateProductCommandValidator.cs new file mode 100644 index 0000000..afc7cbf --- /dev/null +++ b/src/Imprink.Application/Validation/Products/UpdateProductCommandValidator.cs @@ -0,0 +1,38 @@ +using FluentValidation; +using Imprink.Application.Domains.Products; + +namespace Imprink.Application.Validation.Products; + +public class UpdateProductCommandValidator : AbstractValidator +{ + public UpdateProductCommandValidator() + { + RuleFor(x => x.Id) + .NotEmpty().WithMessage("Id is required.") + .NotEqual(Guid.Empty).WithMessage("Id must be a valid GUID."); + + RuleFor(x => x.Name) + .NotEmpty().WithMessage("Name is required.") + .Length(1, 100).WithMessage("Name must be between 1 and 100 characters."); + + RuleFor(x => x.Description) + .Length(1, 1000).WithMessage("Description must be between 1 and 1000 characters.") + .When(x => !string.IsNullOrWhiteSpace(x.Description)); + + RuleFor(x => x.BasePrice) + .GreaterThan(0).WithMessage("BasePrice must be greater than 0."); + + RuleFor(x => x.ImageUrl) + .Must(BeValidUrl).When(x => !string.IsNullOrWhiteSpace(x.ImageUrl)) + .WithMessage("ImageUrl must be a valid URL."); + + RuleFor(x => x.CategoryId) + .NotEqual(Guid.Empty).When(x => x.CategoryId.HasValue) + .WithMessage("CategoryId must be a valid GUID."); + } + + private static bool BeValidUrl(string? url) + { + return Uri.TryCreate(url, UriKind.Absolute, out _); + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Validation/Users/SetUserFullNameCommandValidator.cs b/src/Imprink.Application/Validation/Users/SetUserFullNameCommandValidator.cs new file mode 100644 index 0000000..b97eed6 --- /dev/null +++ b/src/Imprink.Application/Validation/Users/SetUserFullNameCommandValidator.cs @@ -0,0 +1,18 @@ +using FluentValidation; +using Imprink.Application.Domains.Users; + +namespace Imprink.Application.Validation.Users; + +public class SetUserFullNameCommandValidator : AbstractValidator +{ + public SetUserFullNameCommandValidator() + { + RuleFor(x => x.FirstName) + .NotEmpty().WithMessage("FirstName is required.") + .Length(1, 50).WithMessage("FirstName must be between 1 and 50 characters."); + + RuleFor(x => x.LastName) + .NotEmpty().WithMessage("LastName is required.") + .Length(1, 50).WithMessage("LastName must be between 1 and 50 characters."); + } +} \ No newline at end of file diff --git a/src/Imprink.Application/Validation/Users/SetUserPhoneCommandValidator.cs b/src/Imprink.Application/Validation/Users/SetUserPhoneCommandValidator.cs new file mode 100644 index 0000000..54a73a8 --- /dev/null +++ b/src/Imprink.Application/Validation/Users/SetUserPhoneCommandValidator.cs @@ -0,0 +1,14 @@ +using FluentValidation; +using Imprink.Application.Domains.Users; + +namespace Imprink.Application.Validation.Users; + +public class SetUserPhoneCommandValidator : AbstractValidator +{ + public SetUserPhoneCommandValidator() + { + RuleFor(x => x.PhoneNumber) + .NotEmpty().WithMessage("PhoneNumber is required.") + .Matches(@"^\+?[1-9]\d{1,14}$").WithMessage("PhoneNumber must be a valid phone number format."); + } +} \ No newline at end of file diff --git a/src/Imprink.Domain/Imprink.Domain.csproj b/src/Imprink.Domain/Imprink.Domain.csproj index 3302df9..1584c08 100644 --- a/src/Imprink.Domain/Imprink.Domain.csproj +++ b/src/Imprink.Domain/Imprink.Domain.csproj @@ -10,4 +10,8 @@ + + + + diff --git a/src/Imprink.Infrastructure/Imprink.Infrastructure.csproj b/src/Imprink.Infrastructure/Imprink.Infrastructure.csproj index 8650de1..8bf4ad7 100644 --- a/src/Imprink.Infrastructure/Imprink.Infrastructure.csproj +++ b/src/Imprink.Infrastructure/Imprink.Infrastructure.csproj @@ -25,6 +25,7 @@ + diff --git a/src/Imprink.Infrastructure/Services/CurrentUserService.cs b/src/Imprink.Infrastructure/Services/CurrentUserService.cs index 6579e54..89ffbe9 100644 --- a/src/Imprink.Infrastructure/Services/CurrentUserService.cs +++ b/src/Imprink.Infrastructure/Services/CurrentUserService.cs @@ -1,5 +1,5 @@ using System.Security.Claims; -using Imprink.Application.Service; +using Imprink.Application.Services; using Microsoft.AspNetCore.Http; namespace Imprink.Infrastructure.Services; diff --git a/src/Imprink.WebApi/Controllers/Products/CategoriesController.cs b/src/Imprink.WebApi/Controllers/Products/CategoriesController.cs index 33a28de..ba087ae 100644 --- a/src/Imprink.WebApi/Controllers/Products/CategoriesController.cs +++ b/src/Imprink.WebApi/Controllers/Products/CategoriesController.cs @@ -1,6 +1,7 @@ +using Imprink.Application.Domains.Categories; using Imprink.Application.Products.Dtos; -using Imprink.Application.Products.Query; using MediatR; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Imprink.WebApi.Controllers; @@ -10,8 +11,38 @@ namespace Imprink.WebApi.Controllers; public class CategoriesController(IMediator mediator) : ControllerBase { [HttpGet] - public async Task>> GetCategories([FromQuery] GetCategoriesQuery query) + [AllowAnonymous] + public async Task>> GetCategories( + [FromQuery] GetCategoriesQuery query) { return Ok(await mediator.Send(query)); } + + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task> CreateCategory( + [FromBody] CreateCategoryCommand command) + { + var result = await mediator.Send(command); + return CreatedAtAction(nameof(GetCategories), new { id = result.Id }, result); + } + + [HttpPut("{id:guid}")] + [Authorize(Roles = "Admin")] + public async Task> UpdateCategory( + Guid id, + [FromBody] UpdateCategoryCommand command) + { + if (id != command.Id) return BadRequest("ID mismatch"); + + return Ok(await mediator.Send(command)); + } + + [HttpDelete("{id:guid}")] + [Authorize(Roles = "Admin")] + public async Task DeleteCategory(Guid id) + { + await mediator.Send(new DeleteCategoryCommand { Id = id }); + return NoContent(); + } } \ No newline at end of file diff --git a/src/Imprink.WebApi/Controllers/Products/ProductVariantsController.cs b/src/Imprink.WebApi/Controllers/Products/ProductVariantsController.cs index 95950ba..745de3e 100644 --- a/src/Imprink.WebApi/Controllers/Products/ProductVariantsController.cs +++ b/src/Imprink.WebApi/Controllers/Products/ProductVariantsController.cs @@ -1,6 +1,7 @@ +using Imprink.Application.Domains.ProductVariants; using Imprink.Application.Products.Dtos; -using Imprink.Application.Products.Query; using MediatR; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; namespace Imprink.WebApi.Controllers; @@ -10,9 +11,38 @@ namespace Imprink.WebApi.Controllers; public class ProductVariantsController(IMediator mediator) : ControllerBase { [HttpGet] + [AllowAnonymous] public async Task>> GetProductVariants( [FromQuery] GetProductVariantsQuery query) { return Ok(await mediator.Send(query)); } + + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task> CreateProductVariant( + [FromBody] CreateProductVariantCommand command) + { + var result = await mediator.Send(command); + return CreatedAtAction(nameof(GetProductVariants), new { id = result.Id }, result); + } + + [HttpPut("{id:guid}")] + [Authorize(Roles = "Admin")] + public async Task> UpdateProductVariant( + Guid id, + [FromBody] UpdateProductVariantCommand command) + { + if (id != command.Id) return BadRequest("ID mismatch"); + + return Ok(await mediator.Send(command)); + } + + [HttpDelete("{id:guid}")] + [Authorize(Roles = "Admin")] + public async Task DeleteProductVariant(Guid id) + { + await mediator.Send(new DeleteProductVariantCommand { Id = id }); + return NoContent(); + } } \ No newline at end of file diff --git a/src/Imprink.WebApi/Controllers/Products/ProductsController.cs b/src/Imprink.WebApi/Controllers/Products/ProductsController.cs index 6567082..e1f7fc5 100644 --- a/src/Imprink.WebApi/Controllers/Products/ProductsController.cs +++ b/src/Imprink.WebApi/Controllers/Products/ProductsController.cs @@ -1,3 +1,4 @@ +using Imprink.Application.Domains.Products; using Imprink.Application.Products; using Imprink.Application.Products.Dtos; using Imprink.Domain.Models; @@ -20,4 +21,33 @@ public class ProductsController(IMediator mediator) : ControllerBase var result = await mediator.Send(new GetProductsQuery { FilterParameters = filterParameters}); return Ok(result); } + + [HttpPost] + [Authorize(Roles = "Admin")] + public async Task>> CreateProduct( + [FromBody] CreateProductCommand command) + { + var result = await mediator.Send(command); + return Ok(result); + } + + [HttpPut("{id:guid}")] + [Authorize(Roles = "Admin")] + public async Task> UpdateProduct( + Guid id, + [FromBody] UpdateProductCommand command) + { + if (id != command.Id) return BadRequest("ID mismatch"); + + var result = await mediator.Send(command); + return Ok(result); + } + + [HttpDelete("{id:guid}")] + [Authorize(Roles = "Admin")] + public async Task DeleteProduct(Guid id) + { + await mediator.Send(new DeleteProductCommand { Id = id }); + return NoContent(); + } } \ No newline at end of file diff --git a/src/Imprink.WebApi/Controllers/Users/UsersController.cs b/src/Imprink.WebApi/Controllers/Users/UsersController.cs index 606f313..53b6830 100644 --- a/src/Imprink.WebApi/Controllers/Users/UsersController.cs +++ b/src/Imprink.WebApi/Controllers/Users/UsersController.cs @@ -1,5 +1,5 @@ using System.Security.Claims; -using Imprink.Application.Users; +using Imprink.Application.Domains.Users; using Imprink.Domain.Models; using MediatR; using Microsoft.AspNetCore.Authorization; diff --git a/src/Imprink.WebApi/Startup.cs b/src/Imprink.WebApi/Startup.cs index 3ea0697..b7af072 100644 --- a/src/Imprink.WebApi/Startup.cs +++ b/src/Imprink.WebApi/Startup.cs @@ -1,8 +1,9 @@ using System.Security.Claims; using FluentValidation; using Imprink.Application; -using Imprink.Application.Products.Create; -using Imprink.Application.Service; +using Imprink.Application.Domains.Products; +using Imprink.Application.Products; +using Imprink.Application.Services; using Imprink.Application.Validation.Models; using Imprink.Domain.Repositories; using Imprink.Domain.Repositories.Products;