diff --git a/postman/dotnet.webapi.boilerplate.postman_collection.json b/postman/dotnet.webapi.boilerplate.postman_collection.json index db87c041b..326ed8137 100644 --- a/postman/dotnet.webapi.boilerplate.postman_collection.json +++ b/postman/dotnet.webapi.boilerplate.postman_collection.json @@ -1,6 +1,6 @@ { "info": { - "_postman_id": "7bc4b7a3-1161-4775-946b-1c2d70f3c34b", + "_postman_id": "2059afd5-1d5c-4093-a29b-c3e16a45c202", "name": "dotnet.webapi.boilerplate", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" }, @@ -252,6 +252,39 @@ } }, "response": [] + }, + { + "name": "update-product", + "request": { + "method": "PUT", + "header": [], + "body": { + "mode": "raw", + "raw": "{\r\n \"name\":\"Product Test to max name length to be exactly or around 75 which is the max\",\r\n \"description\":\"Something more Cool!\",\r\n \"rate\":5\r\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "{{url}}/v1/products?id=a5d1b2e5-295c-4ebd-a80e-fb6b82b1cb55", + "host": [ + "{{url}}" + ], + "path": [ + "v1", + "products" + ], + "query": [ + { + "key": "id", + "value": "a5d1b2e5-295c-4ebd-a80e-fb6b82b1cb55" + } + ] + } + }, + "response": [] } ] }, diff --git a/src/Bootstrapper/Controllers/v1/ProductsController.cs b/src/Bootstrapper/Controllers/v1/ProductsController.cs index 4a801c081..74a6b0443 100644 --- a/src/Bootstrapper/Controllers/v1/ProductsController.cs +++ b/src/Bootstrapper/Controllers/v1/ProductsController.cs @@ -47,6 +47,12 @@ public async Task CreateAsync(CreateProductRequest request) return Ok(await _service.CreateProductAsync(request)); } + [HttpPut] + public async Task UpdateAsync(CreateProductRequest request, Guid id) + { + return Ok(await _service.UpdateProductAsync(request, id)); + } + [HttpDelete] public async Task DeleteAsync(Guid id) { diff --git a/src/Core/Application/Abstractions/Services/Catalog/IProductService.cs b/src/Core/Application/Abstractions/Services/Catalog/IProductService.cs index f26c699db..b372636b0 100644 --- a/src/Core/Application/Abstractions/Services/Catalog/IProductService.cs +++ b/src/Core/Application/Abstractions/Services/Catalog/IProductService.cs @@ -12,6 +12,7 @@ public interface IProductService : ITransientService Task> GetByIdUsingDapperAsync(Guid id); Task> GetListAsync(ProductListFilter filter); Task> CreateProductAsync(CreateProductRequest request); + Task> UpdateProductAsync(CreateProductRequest request, Guid id); Task> DeleteProductAsync(Guid id); } } \ No newline at end of file diff --git a/src/Core/Application/Services/Catalog/ProductService.cs b/src/Core/Application/Services/Catalog/ProductService.cs index 368c5952f..6a08889bd 100644 --- a/src/Core/Application/Services/Catalog/ProductService.cs +++ b/src/Core/Application/Services/Catalog/ProductService.cs @@ -37,6 +37,31 @@ public async Task> CreateProductAsync(CreateProductRequest reques return await Result.SuccessAsync(productId); } + public async Task> UpdateProductAsync(CreateProductRequest request, Guid id) + { + var product = await _repository.GetByIdAsync(id); + + if(product == null) + { + return await Result.FailAsync("Product Id provided is invalid, it doesn't return a product."); + } + + string productImagePath = string.Empty; + + // If image is provided process upload + if (request.Image != null) + { + productImagePath = await _file.UploadAsync(request.Image, FileType.Image); + } + + var updateProduct = product.Update(request.Name, request.Description, request.Rate, productImagePath); + + await _repository.UpdateAsync(updateProduct); + await _repository.SaveChangesAsync(); + + return await Result.SuccessAsync(updateProduct); + } + public async Task> DeleteProductAsync(Guid id) { await _repository.RemoveByIdAsync(id); diff --git a/src/Core/Application/Validators/Catalog/UpdateProductRequestValidator.cs b/src/Core/Application/Validators/Catalog/UpdateProductRequestValidator.cs new file mode 100644 index 000000000..78f6ceeea --- /dev/null +++ b/src/Core/Application/Validators/Catalog/UpdateProductRequestValidator.cs @@ -0,0 +1,16 @@ +using DN.WebApi.Application.Validators.General; +using DN.WebApi.Shared.DTOs.Catalog; +using FluentValidation; + +namespace DN.WebApi.Application.Validators.Catalog +{ + public class UpdateProductRequestValidator : CustomValidator + { + public UpdateProductRequestValidator() + { + RuleFor(p => p.Name).MaximumLength(75).NotEmpty(); + RuleFor(p => p.Rate).GreaterThanOrEqualTo(1).NotEqual(0); + RuleFor(p => p.Image).SetValidator(new FileUploadRequestValidator()); + } + } +} \ No newline at end of file diff --git a/src/Core/Domain/Entities/Catalog/Product.cs b/src/Core/Domain/Entities/Catalog/Product.cs index 2c85f35f6..22f174d42 100644 --- a/src/Core/Domain/Entities/Catalog/Product.cs +++ b/src/Core/Domain/Entities/Catalog/Product.cs @@ -16,6 +16,21 @@ protected Product() { } + public Product Update(string name, string description, decimal rate, string imagePath) + { + Name = name; + Description = description; + Rate = rate; + + // Update image path if one is provided + if (!string.IsNullOrEmpty(imagePath)) + { + ImagePath = imagePath; + } + + return this; + } + public string Name { get; private set; } public string Description { get; private set; } diff --git a/src/Shared/Shared.DTOs/Catalog/UpdateProductRequest.cs b/src/Shared/Shared.DTOs/Catalog/UpdateProductRequest.cs new file mode 100644 index 000000000..696ae4746 --- /dev/null +++ b/src/Shared/Shared.DTOs/Catalog/UpdateProductRequest.cs @@ -0,0 +1,12 @@ +using DN.WebApi.Shared.DTOs.General.Requests; + +namespace DN.WebApi.Shared.DTOs.Catalog +{ + public class UpdateProductRequest : IMustBeValid + { + public string Name { get; set; } + public string Description { get; set; } + public decimal Rate { get; set; } + public FileUploadRequest Image { get; set; } + } +} \ No newline at end of file