From d893beb4c7dc094d5af2d864aae990052f9aae1b Mon Sep 17 00:00:00 2001 From: Hubert Date: Wed, 4 Oct 2023 11:56:11 +0200 Subject: [PATCH 1/8] create endpoints to add, update, and deactivate a product --- .../Services/v2/IProductService.cs | 6 + .../Services/v2/ProductService.cs | 106 ++++++++++++++++++ .../v2/Product/InitiateProductRequest.cs | 33 ++++++ .../InitiateProductRequestWithUserGroups.cs | 10 ++ .../v2/Product/InitiateProductResponse.cs | 26 +++++ .../Controllers/v2/ProductsController.cs | 93 +++++++++++++++ 6 files changed, 274 insertions(+) create mode 100644 coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequest.cs create mode 100644 coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequestWithUserGroups.cs create mode 100644 coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductResponse.cs create mode 100644 coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs diff --git a/coffeecard/CoffeeCard.Library/Services/v2/IProductService.cs b/coffeecard/CoffeeCard.Library/Services/v2/IProductService.cs index 2bea42cc..c3d17890 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/IProductService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/IProductService.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; +using CoffeeCard.Models.DataTransferObjects.v2.Product; using CoffeeCard.Models.Entities; namespace CoffeeCard.Library.Services.v2 @@ -10,5 +11,10 @@ public interface IProductService : IDisposable Task> GetPublicProductsAsync(); Task> GetProductsForUserAsync(User user); Task GetProductAsync(int productId); + Task AddProduct(InitiateProductRequest product, IEnumerable allowedUserGroups); + + Task UpdateProduct(InitiateProductRequest product); + + Task DeactivateProduct(int productId); } } \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs index 156ec58e..62ecbaa8 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs @@ -3,6 +3,7 @@ using System.Threading.Tasks; using CoffeeCard.Common.Errors; using CoffeeCard.Library.Persistence; +using CoffeeCard.Models.DataTransferObjects.v2.Product; using CoffeeCard.Models.Entities; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; @@ -57,6 +58,111 @@ public async Task GetProductAsync(int productId) return product; } + public Task AddProduct(InitiateProductRequest newProduct, IEnumerable allowedUserGroups) + { + var product = new Product() + { + Price = newProduct.Price, + Description = newProduct.Description, + Name = newProduct.Name, + NumberOfTickets = newProduct.NumberOfTickets, + Visible = newProduct.Visible + }; + + var productUserGroups = allowedUserGroups.Select(userGroup => new ProductUserGroup + { + ProductId = product.Id, + UserGroup = userGroup + }).ToList(); + + _context.ProductUserGroups.AddRange(productUserGroups); + + _context.Products.Add(product); + + _context.SaveChanges(); + + + var result = new InitiateProductResponse + { + Price = product.Price, + Description = product.Description, + Name = product.Name, + NumberOfTickets = product.NumberOfTickets, + Visible = product.Visible + }; + + return Task.FromResult(result); + } + + private Product GetProduct(int productId) + { + return _context.Products.Where(p => p.Id == productId).FirstOrDefault(); + } + + public Task UpdateProduct(InitiateProductRequest changedProduct) + { + var product = GetProduct(changedProduct.Id); + + if (changedProduct.Id != default(int)) + { + Log.Information($"Changing Id of product from {product.Id} to {changedProduct.Id}"); + product.Id = changedProduct.Id; + } + + if (changedProduct.NumberOfTickets != default(int)) + { + Log.Information($"Changing NumberOfTickets of product from {product.NumberOfTickets} to {changedProduct.NumberOfTickets}"); + product.NumberOfTickets = changedProduct.NumberOfTickets; + } + + if (!string.IsNullOrEmpty(changedProduct.Name)) + { + Log.Information($"Changing Name of product from {product.Name} to {changedProduct.Name}"); + product.Name = changedProduct.Name; + } + + if (changedProduct.ExperienceWorth != default(int)) + { + Log.Information($"Changing ExperienceWorth of product from {product.ExperienceWorth} to {changedProduct.ExperienceWorth}"); + product.ExperienceWorth = changedProduct.ExperienceWorth; + } + + if (changedProduct.Visible != default(bool)) + { + Log.Information($"Changing Visible of product from {product.Visible} to {changedProduct.Visible}"); + product.Visible = changedProduct.Visible; + } + + _context.SaveChanges(); + + var result = new InitiateProductResponse + { + Price = product.Price, + Description = product.Description, + Name = product.Name, + NumberOfTickets = product.NumberOfTickets, + Visible = product.Visible + }; + + return Task.FromResult(result); + } + + public async Task DeactivateProduct(int productId) + { + var product = await _context.Products.FindAsync(productId); + + if (product == null) + { + return false; + } + + product.Visible = false; + + await _context.SaveChangesAsync(); + + return true; + } + public void Dispose() { _context?.Dispose(); diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequest.cs new file mode 100644 index 00000000..df4117b8 --- /dev/null +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequest.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; +using CoffeeCard.Models.Entities; + +namespace CoffeeCard.Models.DataTransferObjects.v2.Product +{ + /// + /// Initiate a new product request + /// + public class InitiateProductRequest + { + [Required] + public int Id { get; set; } + + [Required] + public int Price { get; set; } + + public int NumberOfTickets { get; set; } + + [Required] + public string Name { get; set; } + + + [Required] + public string Description { get; set; } + + public int ExperienceWorth { get; set; } + + [DefaultValue(true)] + public bool Visible { get; set; } = true; + } +} \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequestWithUserGroups.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequestWithUserGroups.cs new file mode 100644 index 00000000..8e528b1c --- /dev/null +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequestWithUserGroups.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using CoffeeCard.Models.Entities; + +namespace CoffeeCard.Models.DataTransferObjects.v2.Product; + +public class InitiateProductRequestWithUserGroups +{ + public InitiateProductRequest Product { get; set; } + public IEnumerable AllowedUserGroups { get; set; } +} \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductResponse.cs new file mode 100644 index 00000000..f96e9599 --- /dev/null +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductResponse.cs @@ -0,0 +1,26 @@ +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace CoffeeCard.Models.DataTransferObjects.v2.Product +{ + /// + /// Initiate a new product request + /// + public class InitiateProductResponse + { + [Required] + public int Price { get; set; } + + public int NumberOfTickets { get; set; } + + [Required] + public string Name { get; set; } + + [Required] + public string Description { get; set; } + + [Required] + public bool Visible { get; set; } + } +} \ No newline at end of file diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs new file mode 100644 index 00000000..4c2874d2 --- /dev/null +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs @@ -0,0 +1,93 @@ +using System.Collections.Generic; +using System.Threading.Tasks; +using CoffeeCard.Library.Services; +using CoffeeCard.Library.Services.v2; +using CoffeeCard.Library.Utils; +using CoffeeCard.Models.DataTransferObjects.v2.Leaderboard; +using CoffeeCard.Models.DataTransferObjects.v2.Product; +using CoffeeCard.Models.Entities; +using CoffeeCard.WebApi.Helpers; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using IProductService = CoffeeCard.Library.Services.v2.IProductService; + +namespace CoffeeCard.WebApi.Controllers.v2 +{ + /// + /// Controller for creating, changing, and deactivating a product + /// + [ApiController] + [Authorize] + [ApiVersion("2")] + [Route("api/v{version:apiVersion}/products")] + + public class ProductsController : ControllerBase + { + private readonly ClaimsUtilities _claimsUtilities; + private readonly IMapperService _mapperService; + private readonly IProductService _productService; + + /// + /// Initializes a new instance of the class. + /// + public ProductsController(IProductService productService, IMapperService mapper, + ClaimsUtilities claimsUtilities) + { + _productService = productService; + _mapperService = mapper; + _claimsUtilities = claimsUtilities; + } + + /// + /// Adds a new product to the database. + /// + /// The request containing the details of the product to be added and allowed user groups. + /// The newly added product wrapped in a InitiateProductResponse object. + /// The request was successful, and the product was added. + [HttpPost("add")] + [AuthorizeRoles(UserGroup.Board)] + [ProducesResponseType(typeof(InitiateProductResponse), StatusCodes.Status200OK)] + public async Task AddProduct(InitiateProductRequestWithUserGroups initiateProductRequest) + { + return Ok(await _productService.AddProduct(initiateProductRequest.Product, initiateProductRequest.AllowedUserGroups)); + } + + + /// + /// Updates a product with the specified changes. + /// + /// The request containing the changes to be applied to the product. + /// A response indicating the result of the update operation. + /// The request was successful, and the product was updated. + [HttpGet("update")] + [AuthorizeRoles(UserGroup.Board)] + [ProducesResponseType(typeof(InitiateProductResponse), StatusCodes.Status200OK)] + public async Task UpdateProduct(InitiateProductRequest product) + { + return Ok(await _productService.UpdateProduct(product)); + } + + + /// + /// Deactivates a product by setting its visibility to false. + /// + /// The ID of the product to be deactivated. + /// A response indicating the result of the deactivation operation. + /// The request was successful, and the product was deactivated. + /// The product with the specified ID was not found. + [HttpPost("deactivate/{productId}")] + [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] + [ProducesResponseType(StatusCodes.Status404NotFound)] + public async Task DeactivateProduct(int productId) + { + var result = await _productService.DeactivateProduct(productId); + + if (result) + { + return Ok(); + } + return NotFound(); + } + } +} \ No newline at end of file From 8326cfd615786dc4d9d2407069f8edd1ee048392 Mon Sep 17 00:00:00 2001 From: Hubert Date: Wed, 11 Oct 2023 14:03:29 +0200 Subject: [PATCH 2/8] fix the comments + remove a method to deactivate a product, as you can do that using the updateProduct method --- .../Services/v2/IProductService.cs | 6 +- .../Services/v2/ProductService.cs | 65 +++++++------------ .../v2/Product/AddProductRequest.cs | 26 ++++++++ ....cs => AddProductRequestWithUserGroups.cs} | 4 +- ...eProductResponse.cs => ProductResponse.cs} | 2 +- ...ductRequest.cs => UpdateProductRequest.cs} | 10 +-- .../Controllers/v2/ProductsController.cs | 43 +++--------- 7 files changed, 65 insertions(+), 91 deletions(-) create mode 100644 coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs rename coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/{InitiateProductRequestWithUserGroups.cs => AddProductRequestWithUserGroups.cs} (64%) rename coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/{InitiateProductResponse.cs => ProductResponse.cs} (93%) rename coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/{InitiateProductRequest.cs => UpdateProductRequest.cs} (78%) diff --git a/coffeecard/CoffeeCard.Library/Services/v2/IProductService.cs b/coffeecard/CoffeeCard.Library/Services/v2/IProductService.cs index c3d17890..428ffb93 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/IProductService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/IProductService.cs @@ -11,10 +11,8 @@ public interface IProductService : IDisposable Task> GetPublicProductsAsync(); Task> GetProductsForUserAsync(User user); Task GetProductAsync(int productId); - Task AddProduct(InitiateProductRequest product, IEnumerable allowedUserGroups); + Task AddProduct(AddProductRequest product, IEnumerable allowedUserGroups); - Task UpdateProduct(InitiateProductRequest product); - - Task DeactivateProduct(int productId); + Task UpdateProduct(UpdateProductRequest product); } } \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs index 62ecbaa8..1e1e896e 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs @@ -58,7 +58,7 @@ public async Task GetProductAsync(int productId) return product; } - public Task AddProduct(InitiateProductRequest newProduct, IEnumerable allowedUserGroups) + public async Task AddProduct(AddProductRequest newProduct, IEnumerable allowedUserGroups) { var product = new Product() { @@ -66,9 +66,13 @@ public Task AddProduct(InitiateProductRequest newProduc Description = newProduct.Description, Name = newProduct.Name, NumberOfTickets = newProduct.NumberOfTickets, + ExperienceWorth = 0, Visible = newProduct.Visible }; + _context.Products.Add(product); + await _context.SaveChangesAsync(); + var productUserGroups = allowedUserGroups.Select(userGroup => new ProductUserGroup { ProductId = product.Id, @@ -77,12 +81,11 @@ public Task AddProduct(InitiateProductRequest newProduc _context.ProductUserGroups.AddRange(productUserGroups); - _context.Products.Add(product); - _context.SaveChanges(); + await _context.SaveChangesAsync(); - var result = new InitiateProductResponse + var result = new ProductResponse { Price = product.Price, Description = product.Description, @@ -91,24 +94,24 @@ public Task AddProduct(InitiateProductRequest newProduc Visible = product.Visible }; - return Task.FromResult(result); - } - - private Product GetProduct(int productId) - { - return _context.Products.Where(p => p.Id == productId).FirstOrDefault(); + return result; } - public Task UpdateProduct(InitiateProductRequest changedProduct) + public async Task UpdateProduct(UpdateProductRequest changedProduct) { - var product = GetProduct(changedProduct.Id); - - if (changedProduct.Id != default(int)) + var product = await GetProductAsync(changedProduct.Id); + + if (changedProduct.Price != default(int)) { - Log.Information($"Changing Id of product from {product.Id} to {changedProduct.Id}"); - product.Id = changedProduct.Id; + Log.Information($"Changing Price of product from {product.NumberOfTickets} to {changedProduct.NumberOfTickets}"); + product.Price = changedProduct.Price; } - + if (!string.IsNullOrEmpty(changedProduct.Description)) + { + Log.Information($"Changing Description of product from {product.Description} to {changedProduct.Description}"); + product.Description = changedProduct.Description; + } + if (changedProduct.NumberOfTickets != default(int)) { Log.Information($"Changing NumberOfTickets of product from {product.NumberOfTickets} to {changedProduct.NumberOfTickets}"); @@ -121,21 +124,15 @@ public Task UpdateProduct(InitiateProductRequest change product.Name = changedProduct.Name; } - if (changedProduct.ExperienceWorth != default(int)) - { - Log.Information($"Changing ExperienceWorth of product from {product.ExperienceWorth} to {changedProduct.ExperienceWorth}"); - product.ExperienceWorth = changedProduct.ExperienceWorth; - } - if (changedProduct.Visible != default(bool)) { Log.Information($"Changing Visible of product from {product.Visible} to {changedProduct.Visible}"); product.Visible = changedProduct.Visible; } - _context.SaveChanges(); + await _context.SaveChangesAsync(); - var result = new InitiateProductResponse + var result = new ProductResponse { Price = product.Price, Description = product.Description, @@ -144,23 +141,7 @@ public Task UpdateProduct(InitiateProductRequest change Visible = product.Visible }; - return Task.FromResult(result); - } - - public async Task DeactivateProduct(int productId) - { - var product = await _context.Products.FindAsync(productId); - - if (product == null) - { - return false; - } - - product.Visible = false; - - await _context.SaveChangesAsync(); - - return true; + return result; } public void Dispose() diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs new file mode 100644 index 00000000..4bc3fd5e --- /dev/null +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs @@ -0,0 +1,26 @@ +using System.ComponentModel; +using System.ComponentModel.DataAnnotations; + +namespace CoffeeCard.Models.DataTransferObjects.v2.Product +{ + /// + /// Initiate a new product add request + /// + public class AddProductRequest + { + [Required] + public int Price { get; set; } + + [Required] + public int NumberOfTickets { get; set; } + + [Required] + public string Name { get; set; } + + [Required] + public string Description { get; set; } + + [DefaultValue(true)] + public bool Visible { get; set; } = true; + } +} \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequestWithUserGroups.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequestWithUserGroups.cs similarity index 64% rename from coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequestWithUserGroups.cs rename to coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequestWithUserGroups.cs index 8e528b1c..7aef8bdb 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequestWithUserGroups.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequestWithUserGroups.cs @@ -3,8 +3,8 @@ namespace CoffeeCard.Models.DataTransferObjects.v2.Product; -public class InitiateProductRequestWithUserGroups +public class AddProductRequestWithUserGroups { - public InitiateProductRequest Product { get; set; } + public AddProductRequest Product { get; set; } public IEnumerable AllowedUserGroups { get; set; } } \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs similarity index 93% rename from coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductResponse.cs rename to coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs index f96e9599..40564195 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductResponse.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs @@ -7,7 +7,7 @@ namespace CoffeeCard.Models.DataTransferObjects.v2.Product /// /// Initiate a new product request /// - public class InitiateProductResponse + public class ProductResponse { [Required] public int Price { get; set; } diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs similarity index 78% rename from coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequest.cs rename to coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs index df4117b8..c1d7b4d9 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/InitiateProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs @@ -1,14 +1,12 @@ -using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; -using CoffeeCard.Models.Entities; namespace CoffeeCard.Models.DataTransferObjects.v2.Product { /// /// Initiate a new product request /// - public class InitiateProductRequest + public class UpdateProductRequest { [Required] public int Id { get; set; } @@ -16,17 +14,15 @@ public class InitiateProductRequest [Required] public int Price { get; set; } + [Required] public int NumberOfTickets { get; set; } [Required] public string Name { get; set; } - - + [Required] public string Description { get; set; } - public int ExperienceWorth { get; set; } - [DefaultValue(true)] public bool Visible { get; set; } = true; } diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs index 4c2874d2..a41e8bb7 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs @@ -24,33 +24,28 @@ namespace CoffeeCard.WebApi.Controllers.v2 public class ProductsController : ControllerBase { - private readonly ClaimsUtilities _claimsUtilities; - private readonly IMapperService _mapperService; private readonly IProductService _productService; /// /// Initializes a new instance of the class. /// - public ProductsController(IProductService productService, IMapperService mapper, - ClaimsUtilities claimsUtilities) + public ProductsController(IProductService productService) { _productService = productService; - _mapperService = mapper; - _claimsUtilities = claimsUtilities; } /// /// Adds a new product to the database. /// - /// The request containing the details of the product to be added and allowed user groups. + /// The request containing the details of the product to be added and allowed user groups. /// The newly added product wrapped in a InitiateProductResponse object. /// The request was successful, and the product was added. [HttpPost("add")] [AuthorizeRoles(UserGroup.Board)] - [ProducesResponseType(typeof(InitiateProductResponse), StatusCodes.Status200OK)] - public async Task AddProduct(InitiateProductRequestWithUserGroups initiateProductRequest) + [ProducesResponseType(typeof(ProductResponse), StatusCodes.Status200OK)] + public async Task AddProduct(AddProductRequestWithUserGroups addProductRequest) { - return Ok(await _productService.AddProduct(initiateProductRequest.Product, initiateProductRequest.AllowedUserGroups)); + return Ok(await _productService.AddProduct(addProductRequest.Product, addProductRequest.AllowedUserGroups)); } @@ -60,34 +55,12 @@ public async Task AddProduct(InitiateProductRequestWithUserGroups /// The request containing the changes to be applied to the product. /// A response indicating the result of the update operation. /// The request was successful, and the product was updated. - [HttpGet("update")] + [HttpPut("update")] [AuthorizeRoles(UserGroup.Board)] - [ProducesResponseType(typeof(InitiateProductResponse), StatusCodes.Status200OK)] - public async Task UpdateProduct(InitiateProductRequest product) + [ProducesResponseType(typeof(ProductResponse), StatusCodes.Status200OK)] + public async Task UpdateProduct(UpdateProductRequest product) { return Ok(await _productService.UpdateProduct(product)); } - - - /// - /// Deactivates a product by setting its visibility to false. - /// - /// The ID of the product to be deactivated. - /// A response indicating the result of the deactivation operation. - /// The request was successful, and the product was deactivated. - /// The product with the specified ID was not found. - [HttpPost("deactivate/{productId}")] - [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] - [ProducesResponseType(StatusCodes.Status404NotFound)] - public async Task DeactivateProduct(int productId) - { - var result = await _productService.DeactivateProduct(productId); - - if (result) - { - return Ok(); - } - return NotFound(); - } } } \ No newline at end of file From 27b314ba448315c4f1abe5db2b12712f8480ffa1 Mon Sep 17 00:00:00 2001 From: Hubert Date: Wed, 11 Oct 2023 14:09:44 +0200 Subject: [PATCH 3/8] reformat the code --- .../Services/v2/ProductService.cs | 16 ++++++++-------- .../v2/Product/AddProductRequest.cs | 2 +- .../Product/AddProductRequestWithUserGroups.cs | 4 ++-- .../v2/Product/ProductResponse.cs | 4 ++-- .../v2/Product/UpdateProductRequest.cs | 4 ++-- .../Controllers/v2/ProductsController.cs | 8 ++++---- 6 files changed, 19 insertions(+), 19 deletions(-) diff --git a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs index 1e1e896e..41d7d224 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs @@ -69,7 +69,7 @@ public async Task AddProduct(AddProductRequest newProduct, IEnu ExperienceWorth = 0, Visible = newProduct.Visible }; - + _context.Products.Add(product); await _context.SaveChangesAsync(); @@ -80,8 +80,8 @@ public async Task AddProduct(AddProductRequest newProduct, IEnu }).ToList(); _context.ProductUserGroups.AddRange(productUserGroups); - - + + await _context.SaveChangesAsync(); @@ -96,11 +96,11 @@ public async Task AddProduct(AddProductRequest newProduct, IEnu return result; } - + public async Task UpdateProduct(UpdateProductRequest changedProduct) { var product = await GetProductAsync(changedProduct.Id); - + if (changedProduct.Price != default(int)) { Log.Information($"Changing Price of product from {product.NumberOfTickets} to {changedProduct.NumberOfTickets}"); @@ -111,7 +111,7 @@ public async Task UpdateProduct(UpdateProductRequest changedPro Log.Information($"Changing Description of product from {product.Description} to {changedProduct.Description}"); product.Description = changedProduct.Description; } - + if (changedProduct.NumberOfTickets != default(int)) { Log.Information($"Changing NumberOfTickets of product from {product.NumberOfTickets} to {changedProduct.NumberOfTickets}"); @@ -131,7 +131,7 @@ public async Task UpdateProduct(UpdateProductRequest changedPro } await _context.SaveChangesAsync(); - + var result = new ProductResponse { Price = product.Price, @@ -140,7 +140,7 @@ public async Task UpdateProduct(UpdateProductRequest changedPro NumberOfTickets = product.NumberOfTickets, Visible = product.Visible }; - + return result; } diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs index 4bc3fd5e..e5c1380e 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs @@ -23,4 +23,4 @@ public class AddProductRequest [DefaultValue(true)] public bool Visible { get; set; } = true; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequestWithUserGroups.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequestWithUserGroups.cs index 7aef8bdb..bb98f8b0 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequestWithUserGroups.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequestWithUserGroups.cs @@ -5,6 +5,6 @@ namespace CoffeeCard.Models.DataTransferObjects.v2.Product; public class AddProductRequestWithUserGroups { - public AddProductRequest Product { get; set; } - public IEnumerable AllowedUserGroups { get; set; } + public AddProductRequest Product { get; set; } + public IEnumerable AllowedUserGroups { get; set; } } \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs index 40564195..50dc2e55 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs @@ -16,10 +16,10 @@ public class ProductResponse [Required] public string Name { get; set; } - + [Required] public string Description { get; set; } - + [Required] public bool Visible { get; set; } } diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs index c1d7b4d9..158d519c 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs @@ -19,11 +19,11 @@ public class UpdateProductRequest [Required] public string Name { get; set; } - + [Required] public string Description { get; set; } [DefaultValue(true)] public bool Visible { get; set; } = true; } -} \ No newline at end of file +} \ No newline at end of file diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs index a41e8bb7..74bc2a59 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs @@ -25,7 +25,7 @@ namespace CoffeeCard.WebApi.Controllers.v2 public class ProductsController : ControllerBase { private readonly IProductService _productService; - + /// /// Initializes a new instance of the class. /// @@ -47,8 +47,8 @@ public async Task AddProduct(AddProductRequestWithUserGroups addP { return Ok(await _productService.AddProduct(addProductRequest.Product, addProductRequest.AllowedUserGroups)); } - - + + /// /// Updates a product with the specified changes. /// @@ -62,5 +62,5 @@ public async Task UpdateProduct(UpdateProductRequest product) { return Ok(await _productService.UpdateProduct(product)); } - } + } } \ No newline at end of file From f51ac25a470e7f8128372c879cd277ae5ba793e5 Mon Sep 17 00:00:00 2001 From: Hubert Date: Thu, 12 Oct 2023 18:47:46 +0200 Subject: [PATCH 4/8] add xml docs for swagger and update endpoint path to follow web api practices --- .../v2/Product/AddProductRequest.cs | 26 ++++++++++++++- .../v2/Product/ProductResponse.cs | 27 +++++++++++++++- .../v2/Product/UpdateProductRequest.cs | 32 +++++++++++++++++-- .../Controllers/v2/ProductsController.cs | 4 +-- 4 files changed, 83 insertions(+), 6 deletions(-) diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs index e5c1380e..42740a5c 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs @@ -4,22 +4,46 @@ namespace CoffeeCard.Models.DataTransferObjects.v2.Product { /// - /// Initiate a new product add request + /// Initiate a new product add request. /// + /// + /// { + /// "Price": 100, + /// "NumberOfTickets": 5, + /// "Name": "Latte", + /// "Description": "This is a delicious vegan latte made with premium beans.", + /// "Visible": true + /// } + /// public class AddProductRequest { + /// + /// Gets or sets the price of the product. + /// [Required] public int Price { get; set; } + /// + /// Gets or sets the number of tickets associated with the product. + /// [Required] public int NumberOfTickets { get; set; } + /// + /// Gets or sets the name of the product. + /// [Required] public string Name { get; set; } + /// + /// Gets or sets the description of the product. + /// [Required] public string Description { get; set; } + /// + /// Gets or sets the visibility of the product. Default is true. + /// [DefaultValue(true)] public bool Visible { get; set; } = true; } diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs index 50dc2e55..d08d2f5f 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs @@ -5,21 +5,46 @@ namespace CoffeeCard.Models.DataTransferObjects.v2.Product { /// - /// Initiate a new product request + /// Represents the product response. /// + /// + /// { + /// "Price": 100, + /// "NumberOfTickets": 5, + /// "Name": "Latte", + /// "Description": "A coffee with vegan milk.", + /// "Visible": true + /// } + /// public class ProductResponse { + /// + /// Gets or sets the price of the product. + /// [Required] public int Price { get; set; } + /// + /// Gets or sets the number of tickets associated with the product. + /// + [Required] public int NumberOfTickets { get; set; } + /// + /// Gets or sets the name of the product. + /// [Required] public string Name { get; set; } + /// + /// Gets or sets the description of the product. + /// [Required] public string Description { get; set; } + /// + /// Gets or sets the visibility of the product. + /// [Required] public bool Visible { get; set; } } diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs index 158d519c..2bac0c4e 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs @@ -3,26 +3,54 @@ namespace CoffeeCard.Models.DataTransferObjects.v2.Product { - /// - /// Initiate a new product request + /// /// + /// Initiate an update product request. /// + /// + /// { + /// "Id": 1, + /// "Price": 150, + /// "NumberOfTickets": 10, + /// "Name": "Espresso", + /// "Description": "A coffee made by forcing steam through ground coffee beans.", + /// "Visible": false + /// } + /// public class UpdateProductRequest { + /// + /// Gets or sets the ID of the product to update. + /// [Required] public int Id { get; set; } + /// + /// Gets or sets the updated price of the product. + /// [Required] public int Price { get; set; } + /// + /// Gets or sets the updated number of tickets associated with the product. + /// [Required] public int NumberOfTickets { get; set; } + /// + /// Gets or sets the updated name of the product. + /// [Required] public string Name { get; set; } + /// + /// Gets or sets the updated description of the product. + /// [Required] public string Description { get; set; } + /// + /// Gets or sets the updated visibility of the product. Default is true. + /// [DefaultValue(true)] public bool Visible { get; set; } = true; } diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs index 74bc2a59..c53eb767 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs @@ -40,7 +40,7 @@ public ProductsController(IProductService productService) /// The request containing the details of the product to be added and allowed user groups. /// The newly added product wrapped in a InitiateProductResponse object. /// The request was successful, and the product was added. - [HttpPost("add")] + [HttpPost("")] [AuthorizeRoles(UserGroup.Board)] [ProducesResponseType(typeof(ProductResponse), StatusCodes.Status200OK)] public async Task AddProduct(AddProductRequestWithUserGroups addProductRequest) @@ -55,7 +55,7 @@ public async Task AddProduct(AddProductRequestWithUserGroups addP /// The request containing the changes to be applied to the product. /// A response indicating the result of the update operation. /// The request was successful, and the product was updated. - [HttpPut("update")] + [HttpPut("")] [AuthorizeRoles(UserGroup.Board)] [ProducesResponseType(typeof(ProductResponse), StatusCodes.Status200OK)] public async Task UpdateProduct(UpdateProductRequest product) From 84a4213876d9df33618bdd8af80246511b8abf47 Mon Sep 17 00:00:00 2001 From: Hubert Date: Wed, 25 Oct 2023 15:02:49 +0200 Subject: [PATCH 5/8] fix jonas comments --- .../Services/v2/IProductService.cs | 2 +- .../Services/v2/ProductService.cs | 57 ++++++++----------- .../v2/Product/AddProductRequest.cs | 19 ++++--- .../AddProductRequestWithUserGroups.cs | 10 ---- .../v2/Product/ProductResponse.cs | 13 ++--- .../v2/Product/UpdateProductRequest.cs | 4 ++ .../Controllers/v2/ProductsController.cs | 10 ++-- 7 files changed, 49 insertions(+), 66 deletions(-) delete mode 100644 coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequestWithUserGroups.cs diff --git a/coffeecard/CoffeeCard.Library/Services/v2/IProductService.cs b/coffeecard/CoffeeCard.Library/Services/v2/IProductService.cs index 428ffb93..f5beaed6 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/IProductService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/IProductService.cs @@ -11,7 +11,7 @@ public interface IProductService : IDisposable Task> GetPublicProductsAsync(); Task> GetProductsForUserAsync(User user); Task GetProductAsync(int productId); - Task AddProduct(AddProductRequest product, IEnumerable allowedUserGroups); + Task AddProduct(AddProductRequest product); Task UpdateProduct(UpdateProductRequest product); } diff --git a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs index 41d7d224..175632c2 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Data; using System.Linq; using System.Threading.Tasks; using CoffeeCard.Common.Errors; @@ -35,8 +36,8 @@ private async Task> GetProductsAsync(UserGroup userGroup) return await ( from p in from pug in _context.ProductUserGroups - where pug.UserGroup == userGroup - select pug.Product + where pug.UserGroup == userGroup + select pug.Product where p.Visible orderby p.Id select p @@ -57,9 +58,23 @@ public async Task GetProductAsync(int productId) return product; } + + private async Task CheckProductUniquenessAsync(string name, int price) + { + var product = await _context.Products + .FirstOrDefaultAsync(p => (p.Name == name && p.Price == price)); - public async Task AddProduct(AddProductRequest newProduct, IEnumerable allowedUserGroups) + return product == null; + } + + public async Task AddProduct(AddProductRequest newProduct) { + var unique = await CheckProductUniquenessAsync(newProduct.Name, newProduct.Price); + if (!unique) + { + throw new ConflictException($"Product already exists with name {newProduct.Name} and price of {newProduct.Price}"); + } + var product = new Product() { Price = newProduct.Price, @@ -73,7 +88,7 @@ public async Task AddProduct(AddProductRequest newProduct, IEnu _context.Products.Add(product); await _context.SaveChangesAsync(); - var productUserGroups = allowedUserGroups.Select(userGroup => new ProductUserGroup + var productUserGroups = newProduct.AllowedUserGroups.Select(userGroup => new ProductUserGroup { ProductId = product.Id, UserGroup = userGroup @@ -100,35 +115,11 @@ public async Task AddProduct(AddProductRequest newProduct, IEnu public async Task UpdateProduct(UpdateProductRequest changedProduct) { var product = await GetProductAsync(changedProduct.Id); - - if (changedProduct.Price != default(int)) - { - Log.Information($"Changing Price of product from {product.NumberOfTickets} to {changedProduct.NumberOfTickets}"); - product.Price = changedProduct.Price; - } - if (!string.IsNullOrEmpty(changedProduct.Description)) - { - Log.Information($"Changing Description of product from {product.Description} to {changedProduct.Description}"); - product.Description = changedProduct.Description; - } - - if (changedProduct.NumberOfTickets != default(int)) - { - Log.Information($"Changing NumberOfTickets of product from {product.NumberOfTickets} to {changedProduct.NumberOfTickets}"); - product.NumberOfTickets = changedProduct.NumberOfTickets; - } - - if (!string.IsNullOrEmpty(changedProduct.Name)) - { - Log.Information($"Changing Name of product from {product.Name} to {changedProduct.Name}"); - product.Name = changedProduct.Name; - } - - if (changedProduct.Visible != default(bool)) - { - Log.Information($"Changing Visible of product from {product.Visible} to {changedProduct.Visible}"); - product.Visible = changedProduct.Visible; - } + product.Price = changedProduct.Price; + product.Description = changedProduct.Description; + product.NumberOfTickets = changedProduct.NumberOfTickets; + product.Name = changedProduct.Name; + product.Visible = changedProduct.Visible; await _context.SaveChangesAsync(); diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs index 42740a5c..baec077d 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs @@ -1,44 +1,41 @@ +using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using CoffeeCard.Models.Entities; namespace CoffeeCard.Models.DataTransferObjects.v2.Product { /// /// Initiate a new product add request. /// - /// - /// { - /// "Price": 100, - /// "NumberOfTickets": 5, - /// "Name": "Latte", - /// "Description": "This is a delicious vegan latte made with premium beans.", - /// "Visible": true - /// } - /// public class AddProductRequest { /// /// Gets or sets the price of the product. /// [Required] + [Range(0, int.MaxValue, ErrorMessage = "Price must be a non-negative integer.")] public int Price { get; set; } /// /// Gets or sets the number of tickets associated with the product. /// [Required] + [Range(0, int.MaxValue, ErrorMessage = "Number of Tickets must be a non-negative integer.")] public int NumberOfTickets { get; set; } /// /// Gets or sets the name of the product. /// [Required] + [MinLength(1, ErrorMessage = "Name cannot be an empty string.")] public string Name { get; set; } /// /// Gets or sets the description of the product. /// [Required] + [MinLength(1, ErrorMessage = "Description cannot be an empty string.")] public string Description { get; set; } /// @@ -46,5 +43,9 @@ public class AddProductRequest /// [DefaultValue(true)] public bool Visible { get; set; } = true; + + [Required] + public IEnumerable AllowedUserGroups { get; set; } + } } \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequestWithUserGroups.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequestWithUserGroups.cs deleted file mode 100644 index bb98f8b0..00000000 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequestWithUserGroups.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using CoffeeCard.Models.Entities; - -namespace CoffeeCard.Models.DataTransferObjects.v2.Product; - -public class AddProductRequestWithUserGroups -{ - public AddProductRequest Product { get; set; } - public IEnumerable AllowedUserGroups { get; set; } -} \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs index d08d2f5f..6c9cf26c 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs @@ -7,39 +7,34 @@ namespace CoffeeCard.Models.DataTransferObjects.v2.Product /// /// Represents the product response. /// - /// - /// { - /// "Price": 100, - /// "NumberOfTickets": 5, - /// "Name": "Latte", - /// "Description": "A coffee with vegan milk.", - /// "Visible": true - /// } - /// public class ProductResponse { /// /// Gets or sets the price of the product. /// [Required] + [Range(0, int.MaxValue, ErrorMessage = "Price must be a non-negative integer.")] public int Price { get; set; } /// /// Gets or sets the number of tickets associated with the product. /// [Required] + [Range(0, int.MaxValue, ErrorMessage = "Number of tickets must be a non-negative integer.")] public int NumberOfTickets { get; set; } /// /// Gets or sets the name of the product. /// [Required] + [MinLength(1, ErrorMessage = "Name cannot be an empty string.")] public string Name { get; set; } /// /// Gets or sets the description of the product. /// [Required] + [MinLength(1, ErrorMessage = "Description cannot be an empty string.")] public string Description { get; set; } /// diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs index 2bac0c4e..89fd705b 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs @@ -28,24 +28,28 @@ public class UpdateProductRequest /// Gets or sets the updated price of the product. /// [Required] + [Range(0, int.MaxValue, ErrorMessage = "Price must be a non-negative integer.")] public int Price { get; set; } /// /// Gets or sets the updated number of tickets associated with the product. /// [Required] + [Range(0, int.MaxValue, ErrorMessage = "Number of Tickets must be a non-negative integer.")] public int NumberOfTickets { get; set; } /// /// Gets or sets the updated name of the product. /// [Required] + [MinLength(1, ErrorMessage = "Name cannot be an empty string.")] public string Name { get; set; } /// /// Gets or sets the updated description of the product. /// [Required] + [MinLength(1, ErrorMessage = "Description cannot be an empty string.")] public string Description { get; set; } /// diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs index c53eb767..4ec3dec7 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs @@ -41,11 +41,12 @@ public ProductsController(IProductService productService) /// The newly added product wrapped in a InitiateProductResponse object. /// The request was successful, and the product was added. [HttpPost("")] - [AuthorizeRoles(UserGroup.Board)] + //[AuthorizeRoles(UserGroup.Board)] + [AllowAnonymous] [ProducesResponseType(typeof(ProductResponse), StatusCodes.Status200OK)] - public async Task AddProduct(AddProductRequestWithUserGroups addProductRequest) + public async Task AddProduct(AddProductRequest addProductRequest) { - return Ok(await _productService.AddProduct(addProductRequest.Product, addProductRequest.AllowedUserGroups)); + return Ok(await _productService.AddProduct(addProductRequest)); } @@ -56,7 +57,8 @@ public async Task AddProduct(AddProductRequestWithUserGroups addP /// A response indicating the result of the update operation. /// The request was successful, and the product was updated. [HttpPut("")] - [AuthorizeRoles(UserGroup.Board)] + //[AuthorizeRoles(UserGroup.Board)] + [AllowAnonymous] [ProducesResponseType(typeof(ProductResponse), StatusCodes.Status200OK)] public async Task UpdateProduct(UpdateProductRequest product) { From acfff52341f79bb376771ff4a60eea39b46a8e50 Mon Sep 17 00:00:00 2001 From: Hubert Date: Wed, 25 Oct 2023 15:12:27 +0200 Subject: [PATCH 6/8] minor authorizing fix --- .../CoffeeCard.WebApi/Controllers/v2/ProductsController.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs index 4ec3dec7..b5bc1ccd 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs @@ -41,8 +41,7 @@ public ProductsController(IProductService productService) /// The newly added product wrapped in a InitiateProductResponse object. /// The request was successful, and the product was added. [HttpPost("")] - //[AuthorizeRoles(UserGroup.Board)] - [AllowAnonymous] + [AuthorizeRoles(UserGroup.Board)] [ProducesResponseType(typeof(ProductResponse), StatusCodes.Status200OK)] public async Task AddProduct(AddProductRequest addProductRequest) { @@ -57,8 +56,7 @@ public async Task AddProduct(AddProductRequest addProductRequest) /// A response indicating the result of the update operation. /// The request was successful, and the product was updated. [HttpPut("")] - //[AuthorizeRoles(UserGroup.Board)] - [AllowAnonymous] + [AuthorizeRoles(UserGroup.Board)] [ProducesResponseType(typeof(ProductResponse), StatusCodes.Status200OK)] public async Task UpdateProduct(UpdateProductRequest product) { From 46d04eedc8fe7d9be2950c8d973d509a6893c2f5 Mon Sep 17 00:00:00 2001 From: Hubert Date: Wed, 25 Oct 2023 15:15:20 +0200 Subject: [PATCH 7/8] dotnet factor the code --- .../CoffeeCard.Library/Services/v2/ProductService.cs | 8 ++++---- .../DataTransferObjects/v2/Product/AddProductRequest.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs index 175632c2..4510364e 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs @@ -36,8 +36,8 @@ private async Task> GetProductsAsync(UserGroup userGroup) return await ( from p in from pug in _context.ProductUserGroups - where pug.UserGroup == userGroup - select pug.Product + where pug.UserGroup == userGroup + select pug.Product where p.Visible orderby p.Id select p @@ -58,7 +58,7 @@ public async Task GetProductAsync(int productId) return product; } - + private async Task CheckProductUniquenessAsync(string name, int price) { var product = await _context.Products @@ -74,7 +74,7 @@ public async Task AddProduct(AddProductRequest newProduct) { throw new ConflictException($"Product already exists with name {newProduct.Name} and price of {newProduct.Price}"); } - + var product = new Product() { Price = newProduct.Price, diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs index baec077d..1dc653c4 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs @@ -43,7 +43,7 @@ public class AddProductRequest /// [DefaultValue(true)] public bool Visible { get; set; } = true; - + [Required] public IEnumerable AllowedUserGroups { get; set; } From cbec2a9cc58ff8571bd53bc28f7f38f7f0ebb8f1 Mon Sep 17 00:00:00 2001 From: Hubert Date: Thu, 2 Nov 2023 17:01:34 +0100 Subject: [PATCH 8/8] add swagger xml examples to dto --- .../v2/Product/AddProductRequest.cs | 19 +++++++++++++++++++ .../v2/Product/ProductResponse.cs | 17 +++++++++++++++++ .../v2/Product/UpdateProductRequest.cs | 12 ++++++++++++ 3 files changed, 48 insertions(+) diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs index 1dc653c4..6c42d988 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs @@ -8,11 +8,22 @@ namespace CoffeeCard.Models.DataTransferObjects.v2.Product /// /// Initiate a new product add request. /// + /// + /// { + /// "Name": "Latte", + /// "Price": 25, + /// "NumberOfTickets": 10, + /// "Description": "xxx", + /// "Visible": true + /// } + /// public class AddProductRequest { /// /// Gets or sets the price of the product. /// + /// Product Price + /// 10 [Required] [Range(0, int.MaxValue, ErrorMessage = "Price must be a non-negative integer.")] public int Price { get; set; } @@ -20,6 +31,8 @@ public class AddProductRequest /// /// Gets or sets the number of tickets associated with the product. /// + /// Number of tickets associated with a product + /// 5 [Required] [Range(0, int.MaxValue, ErrorMessage = "Number of Tickets must be a non-negative integer.")] public int NumberOfTickets { get; set; } @@ -27,6 +40,8 @@ public class AddProductRequest /// /// Gets or sets the name of the product. /// + /// Product Name + /// Latte [Required] [MinLength(1, ErrorMessage = "Name cannot be an empty string.")] public string Name { get; set; } @@ -34,6 +49,8 @@ public class AddProductRequest /// /// Gets or sets the description of the product. /// + /// Product Description + /// A homemade latte with soy milk [Required] [MinLength(1, ErrorMessage = "Description cannot be an empty string.")] public string Description { get; set; } @@ -41,6 +58,8 @@ public class AddProductRequest /// /// Gets or sets the visibility of the product. Default is true. /// + /// Product Visibility + /// true [DefaultValue(true)] public bool Visible { get; set; } = true; diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs index 6c9cf26c..00af6b8e 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ProductResponse.cs @@ -12,6 +12,15 @@ public class ProductResponse /// /// Gets or sets the price of the product. /// + /// + /// { + /// "Price": 150, + /// "NumberOfTickets": 10, + /// "Name": "Espresso", + /// "Description": "A coffee made by forcing steam through ground coffee beans.", + /// "Visible": false + /// } + /// [Required] [Range(0, int.MaxValue, ErrorMessage = "Price must be a non-negative integer.")] public int Price { get; set; } @@ -19,6 +28,8 @@ public class ProductResponse /// /// Gets or sets the number of tickets associated with the product. /// + /// Number of Tickets of a Product + /// 5 [Required] [Range(0, int.MaxValue, ErrorMessage = "Number of tickets must be a non-negative integer.")] public int NumberOfTickets { get; set; } @@ -26,6 +37,8 @@ public class ProductResponse /// /// Gets or sets the name of the product. /// + /// Product Name + /// Espresso [Required] [MinLength(1, ErrorMessage = "Name cannot be an empty string.")] public string Name { get; set; } @@ -33,6 +46,8 @@ public class ProductResponse /// /// Gets or sets the description of the product. /// + /// Product Description + /// A homemade espresso from fresh beans [Required] [MinLength(1, ErrorMessage = "Description cannot be an empty string.")] public string Description { get; set; } @@ -40,6 +55,8 @@ public class ProductResponse /// /// Gets or sets the visibility of the product. /// + /// Product Visibility + /// true [Required] public bool Visible { get; set; } } diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs index 89fd705b..a939c1ae 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs @@ -21,12 +21,16 @@ public class UpdateProductRequest /// /// Gets or sets the ID of the product to update. /// + /// Product Id + /// 1 [Required] public int Id { get; set; } /// /// Gets or sets the updated price of the product. /// + /// Product Price + /// 10 [Required] [Range(0, int.MaxValue, ErrorMessage = "Price must be a non-negative integer.")] public int Price { get; set; } @@ -34,6 +38,8 @@ public class UpdateProductRequest /// /// Gets or sets the updated number of tickets associated with the product. /// + /// Number of Tickets of a Product + /// 5 [Required] [Range(0, int.MaxValue, ErrorMessage = "Number of Tickets must be a non-negative integer.")] public int NumberOfTickets { get; set; } @@ -41,6 +47,8 @@ public class UpdateProductRequest /// /// Gets or sets the updated name of the product. /// + /// Product Name + /// Espresso [Required] [MinLength(1, ErrorMessage = "Name cannot be an empty string.")] public string Name { get; set; } @@ -48,6 +56,8 @@ public class UpdateProductRequest /// /// Gets or sets the updated description of the product. /// + /// Product Description + /// A homemade espresso from fresh beans [Required] [MinLength(1, ErrorMessage = "Description cannot be an empty string.")] public string Description { get; set; } @@ -55,6 +65,8 @@ public class UpdateProductRequest /// /// Gets or sets the updated visibility of the product. Default is true. /// + /// Product Visibility + /// true [DefaultValue(true)] public bool Visible { get; set; } = true; }