From ec83cf1912cd5657fc0d3c89d062d1b80472a18a Mon Sep 17 00:00:00 2001 From: Hubert Date: Sat, 11 Nov 2023 10:03:22 +0100 Subject: [PATCH 01/18] add a patch endpoint to update a user group of an account --- .../Services/v2/AccountService.cs | 26 +++++++++++++++++++ .../Services/v2/IAccountService.cs | 9 ++++++- .../Controllers/v2/AccountController.cs | 20 +++++++++++++- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs b/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs index 7bfdbd41..2fbd0e41 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs @@ -8,6 +8,7 @@ using CoffeeCard.Models.DataTransferObjects.v2.User; using CoffeeCard.Models.Entities; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using Serilog; @@ -200,6 +201,31 @@ private async Task GetAccountByEmailAsync(string email) return user; } + public async Task UpdateUserGroup(UserGroup userGroup, int id) + { + User user = await GetUserByIdAsync(id); + + user.UserGroup = userGroup; + + await _context.SaveChangesAsync(); + } + + private async Task GetUserByIdAsync(int id) + { + var user = await _context.Users + .Where(u => u.Id == id) + .FirstOrDefaultAsync(); + + if (user == null) + { + Log.Error("No user was found by user id: {id}", id); + throw new EntityNotFoundException($"No user was found by user id: {id}"); + } + + return user; + } + + private static string EscapeName(string name) { return name.Trim('<', '>', '{', '}'); diff --git a/coffeecard/CoffeeCard.Library/Services/v2/IAccountService.cs b/coffeecard/CoffeeCard.Library/Services/v2/IAccountService.cs index 0c38a49a..ec5dcc3d 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/IAccountService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/IAccountService.cs @@ -4,6 +4,7 @@ using CoffeeCard.Common.Errors; using CoffeeCard.Models.DataTransferObjects.v2.User; using CoffeeCard.Models.Entities; +using Microsoft.AspNetCore.Mvc; namespace CoffeeCard.Library.Services.v2 { @@ -53,8 +54,14 @@ public interface IAccountService /// Resend invite e-mail if user account is not already verified /// /// Email request - /// User account is already verified /// Email account not found Task ResendAccountVerificationEmail(ResendAccountVerificationEmailRequest request); + + /// + /// Update a userGroup of a user with a provided id + /// + /// The user group that will be updated + /// id of the user + Task UpdateUserGroup(UserGroup userGroup, int id); } } \ No newline at end of file diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs index 5713ebc4..0b85d4af 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs @@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Mvc; using CoffeeCard.Library.Services.v2; using CoffeeCard.Models.Entities; -using Serilog; namespace CoffeeCard.WebApi.Controllers.v2 { @@ -135,6 +134,25 @@ public async Task> EmailExists([FromBody] Emai EmailExists = emailInUse }); } + + /// + /// Updates the user group of a user + /// + /// id of the user whose userGroup will be updated + /// UserGroup object that will update the current userGroup attribute + /// Account information + /// The update was processed + /// Invalid credentials + [HttpPatch] + [ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)] + [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [Route("{id:int}/user-group")] + public async Task UpdateAccountUserGroup(int id, UserGroup userGroup) + { + await _accountService.UpdateUserGroup(userGroup, id); + + return new NoContentResult(); + } /// /// Resend account verification email if account is not already verified From db517061947fb80774e796bbf7c3f37f05ebb4b6 Mon Sep 17 00:00:00 2001 From: Hubert Date: Sat, 11 Nov 2023 10:07:22 +0100 Subject: [PATCH 02/18] a fix in xml comments --- .../CoffeeCard.WebApi/Controllers/v2/AccountController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs index 0b85d4af..1ac55958 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs @@ -140,7 +140,7 @@ public async Task> EmailExists([FromBody] Emai /// /// id of the user whose userGroup will be updated /// UserGroup object that will update the current userGroup attribute - /// Account information + /// no content result /// The update was processed /// Invalid credentials [HttpPatch] From 1215be8cfd823b35c078ea34913c14d30a146cbd Mon Sep 17 00:00:00 2001 From: Hubert Date: Sat, 11 Nov 2023 10:09:06 +0100 Subject: [PATCH 03/18] format code --- coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs | 6 +++--- .../CoffeeCard.Library/Services/v2/IAccountService.cs | 2 +- .../CoffeeCard.WebApi/Controllers/v2/AccountController.cs | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs b/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs index 2fbd0e41..fea0d595 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs @@ -204,7 +204,7 @@ private async Task GetAccountByEmailAsync(string email) public async Task UpdateUserGroup(UserGroup userGroup, int id) { User user = await GetUserByIdAsync(id); - + user.UserGroup = userGroup; await _context.SaveChangesAsync(); @@ -215,7 +215,7 @@ private async Task GetUserByIdAsync(int id) var user = await _context.Users .Where(u => u.Id == id) .FirstOrDefaultAsync(); - + if (user == null) { Log.Error("No user was found by user id: {id}", id); @@ -224,7 +224,7 @@ private async Task GetUserByIdAsync(int id) return user; } - + private static string EscapeName(string name) { diff --git a/coffeecard/CoffeeCard.Library/Services/v2/IAccountService.cs b/coffeecard/CoffeeCard.Library/Services/v2/IAccountService.cs index ec5dcc3d..9a35b4f2 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/IAccountService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/IAccountService.cs @@ -56,7 +56,7 @@ public interface IAccountService /// Email request /// Email account not found Task ResendAccountVerificationEmail(ResendAccountVerificationEmailRequest request); - + /// /// Update a userGroup of a user with a provided id /// diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs index 1ac55958..a8894837 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs @@ -134,7 +134,7 @@ public async Task> EmailExists([FromBody] Emai EmailExists = emailInUse }); } - + /// /// Updates the user group of a user /// From cbf89f3935f74bbbb45177cda156beceb700e670 Mon Sep 17 00:00:00 2001 From: A-Guldborg Date: Sun, 12 Nov 2023 02:18:07 +0100 Subject: [PATCH 04/18] Exposes setting product user groups to API --- .../Services/v2/ProductService.cs | 21 ++++++++++++++++--- .../v2/Product/AddProductRequest.cs | 5 +++++ .../v2/Product/ChangedProductResponse.cs | 9 ++++++++ .../v2/Product/UpdateProductRequest.cs | 10 +++++++++ .../v2/Products/ProductResponse.cs | 12 ++++++++++- 5 files changed, 53 insertions(+), 4 deletions(-) diff --git a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs index 666cd4ba..fd3c00bf 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs @@ -95,14 +95,14 @@ public async Task AddProduct(AddProductRequest newProduc await _context.SaveChangesAsync(); - var result = new ChangedProductResponse { Price = product.Price, Description = product.Description, Name = product.Name, NumberOfTickets = product.NumberOfTickets, - Visible = product.Visible + Visible = product.Visible, + AllowedUserGroups = newProduct.AllowedUserGroups }; return result; @@ -119,13 +119,28 @@ public async Task UpdateProduct(UpdateProductRequest cha await _context.SaveChangesAsync(); + var existingUserGroups = _context.ProductUserGroups.Where(e => e.ProductId == changedProduct.Id); + + _context.RemoveRange(existingUserGroups); + + var newProductUserGroups = changedProduct.AllowedUserGroups.Select(userGroup => new ProductUserGroup + { + ProductId = product.Id, + UserGroup = userGroup + }).ToList(); + + _context.AddRange(newProductUserGroups); + + await _context.SaveChangesAsync(); + var result = new ChangedProductResponse { Price = product.Price, Description = product.Description, Name = product.Name, NumberOfTickets = product.NumberOfTickets, - Visible = product.Visible + Visible = product.Visible, + AllowedUserGroups = changedProduct.AllowedUserGroups }; return result; diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs index 6c42d988..e72dd910 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs @@ -63,6 +63,11 @@ public class AddProductRequest [DefaultValue(true)] public bool Visible { get; set; } = true; + /// + /// Gets or sets the user groups that can access the product. + /// + /// Product User Groups + /// Customer [Required] public IEnumerable AllowedUserGroups { get; set; } diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ChangedProductResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ChangedProductResponse.cs index 8a0cfba0..7be12eb7 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ChangedProductResponse.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ChangedProductResponse.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using CoffeeCard.Models.Entities; namespace CoffeeCard.Models.DataTransferObjects.v2.Product { @@ -51,5 +52,13 @@ public class ChangedProductResponse /// true [Required] public bool Visible { get; set; } + + /// + /// Gets or sets the user groups that can access the product. + /// + /// Product User Groups + /// Customer + [Required] + public IEnumerable AllowedUserGroups { get; set; } } } \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs index a939c1ae..f2bcf703 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs @@ -1,5 +1,7 @@ +using System.Collections.Generic; using System.ComponentModel; using System.ComponentModel.DataAnnotations; +using CoffeeCard.Models.Entities; namespace CoffeeCard.Models.DataTransferObjects.v2.Product { @@ -69,5 +71,13 @@ public class UpdateProductRequest /// true [DefaultValue(true)] public bool Visible { get; set; } = true; + + /// + /// Gets or sets the user groups that can access the product. + /// + /// Product User Groups + /// Customer + [Required] + public IEnumerable AllowedUserGroups { get; set; } } } \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Products/ProductResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Products/ProductResponse.cs index b9dfad28..f02e3403 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Products/ProductResponse.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Products/ProductResponse.cs @@ -1,4 +1,6 @@ -using System.ComponentModel.DataAnnotations; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using CoffeeCard.Models.Entities; namespace CoffeeCard.Models.DataTransferObjects.v2.Products { @@ -64,5 +66,13 @@ public class ProductResponse /// true [Required] public bool IsPerk { get; set; } + + /// + /// Decides the user groups that can access the product. + /// + /// Product User Groups + /// Customer + [Required] + public IEnumerable AllowedUserGroups { get; set; } } } From cdf7bc1692372d4cfb871f1c722905cb65a50d67 Mon Sep 17 00:00:00 2001 From: Hubert Date: Thu, 16 Nov 2023 17:47:55 +0100 Subject: [PATCH 05/18] fix Jonas comments --- .../Services/v2/AccountService.cs | 4 ++-- .../v2/User/UpdateUserGroupRequest.cs | 24 +++++++++++++++++++ .../Controllers/v2/AccountController.cs | 13 ++++++---- 3 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/UpdateUserGroupRequest.cs diff --git a/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs b/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs index fea0d595..0b72d071 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/AccountService.cs @@ -201,9 +201,9 @@ private async Task GetAccountByEmailAsync(string email) return user; } - public async Task UpdateUserGroup(UserGroup userGroup, int id) + public async Task UpdateUserGroup(UserGroup userGroup, int userId) { - User user = await GetUserByIdAsync(id); + User user = await GetUserByIdAsync(userId); user.UserGroup = userGroup; diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/UpdateUserGroupRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/UpdateUserGroupRequest.cs new file mode 100644 index 00000000..49fb5c48 --- /dev/null +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/User/UpdateUserGroupRequest.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations; +using CoffeeCard.Models.Entities; + +namespace CoffeeCard.Models.DataTransferObjects.v2.User +{ + /// + /// Update the UserGroup property of a user + /// + /// + /// { + /// "UserGroup": "Barista" + /// } + /// + public class UpdateUserGroupRequest + { + /// + /// The UserGroup of a user + /// + /// UserGroup object + /// UserGroup.Barista + [Required] + public UserGroup UserGroup { get; set; } + } +} \ No newline at end of file diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs index a8894837..fc8cc71c 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs @@ -9,6 +9,7 @@ using Microsoft.AspNetCore.Mvc; using CoffeeCard.Library.Services.v2; using CoffeeCard.Models.Entities; +using CoffeeCard.WebApi.Helpers; namespace CoffeeCard.WebApi.Controllers.v2 { @@ -139,17 +140,21 @@ public async Task> EmailExists([FromBody] Emai /// Updates the user group of a user /// /// id of the user whose userGroup will be updated - /// UserGroup object that will update the current userGroup attribute + /// Update User Group information request /// no content result /// The update was processed /// Invalid credentials + /// User not found [HttpPatch] + [AllowAnonymous] + //[AuthorizeRoles(UserGroup.Board)] [ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)] - [ProducesResponseType(typeof(void), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(ApiError), StatusCodes.Status401Unauthorized)] + [ProducesResponseType(typeof(ApiError), StatusCodes.Status404NotFound)] [Route("{id:int}/user-group")] - public async Task UpdateAccountUserGroup(int id, UserGroup userGroup) + public async Task UpdateAccountUserGroup(int id, [FromBody] UpdateUserGroupRequest updateUserGroupRequest) { - await _accountService.UpdateUserGroup(userGroup, id); + await _accountService.UpdateUserGroup(updateUserGroupRequest.UserGroup, id); return new NoContentResult(); } From 66c9b98c7bfbb9b8cd2451d9689d11db88836a5b Mon Sep 17 00:00:00 2001 From: Hubert Date: Thu, 16 Nov 2023 18:19:49 +0100 Subject: [PATCH 06/18] authorize only board for the patch endpoint --- .../CoffeeCard.WebApi/Controllers/v2/AccountController.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs index fc8cc71c..0bc73a82 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs @@ -167,7 +167,7 @@ public async Task UpdateAccountUserGroup(int id, [FromBody] Update /// Email not found /// Account already verified [HttpPost] - [AllowAnonymous] + [AuthorizeRoles(UserGroup.Board)] [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiError), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ApiError), StatusCodes.Status409Conflict)] From da5215a5b26710b8d65bc893b17a1bb600078fb0 Mon Sep 17 00:00:00 2001 From: Hubert Date: Thu, 16 Nov 2023 18:28:04 +0100 Subject: [PATCH 07/18] uncomment the board authorization line --- .../CoffeeCard.WebApi/Controllers/v2/AccountController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs index 0bc73a82..d8fd0e80 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs @@ -146,8 +146,7 @@ public async Task> EmailExists([FromBody] Emai /// Invalid credentials /// User not found [HttpPatch] - [AllowAnonymous] - //[AuthorizeRoles(UserGroup.Board)] + [AuthorizeRoles(UserGroup.Board)] [ProducesResponseType(typeof(void), StatusCodes.Status204NoContent)] [ProducesResponseType(typeof(ApiError), StatusCodes.Status401Unauthorized)] [ProducesResponseType(typeof(ApiError), StatusCodes.Status404NotFound)] @@ -158,6 +157,7 @@ public async Task UpdateAccountUserGroup(int id, [FromBody] Update return new NoContentResult(); } + /// /// Resend account verification email if account is not already verified From 45b4c450b7297e81eedce26c3f1ed71ce543a2d5 Mon Sep 17 00:00:00 2001 From: Hubert Date: Thu, 16 Nov 2023 18:33:24 +0100 Subject: [PATCH 08/18] format code --- coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs index d8fd0e80..f5971dbe 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs @@ -157,7 +157,6 @@ public async Task UpdateAccountUserGroup(int id, [FromBody] Update return new NoContentResult(); } - /// /// Resend account verification email if account is not already verified From b74fa8210295024ba7ba2cf6225c758a2a66b49a Mon Sep 17 00:00:00 2001 From: A-Guldborg Date: Thu, 23 Nov 2023 16:49:09 +0100 Subject: [PATCH 09/18] Update documentation examples --- .../DataTransferObjects/v2/Product/AddProductRequest.cs | 5 +++-- .../DataTransferObjects/v2/Product/ChangedProductResponse.cs | 2 +- .../DataTransferObjects/v2/Product/UpdateProductRequest.cs | 5 +++-- .../DataTransferObjects/v2/Products/ProductResponse.cs | 5 +++-- 4 files changed, 10 insertions(+), 7 deletions(-) diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs index e72dd910..e9593220 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs @@ -14,7 +14,8 @@ namespace CoffeeCard.Models.DataTransferObjects.v2.Product /// "Price": 25, /// "NumberOfTickets": 10, /// "Description": "xxx", - /// "Visible": true + /// "Visible": true, + /// "AllowedUserGroups": ["Manager", "Board"] /// } /// public class AddProductRequest @@ -67,7 +68,7 @@ public class AddProductRequest /// Gets or sets the user groups that can access the product. /// /// Product User Groups - /// Customer + /// Manager, Board [Required] public IEnumerable AllowedUserGroups { get; set; } diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ChangedProductResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ChangedProductResponse.cs index 7be12eb7..9e2f2011 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ChangedProductResponse.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ChangedProductResponse.cs @@ -57,7 +57,7 @@ public class ChangedProductResponse /// Gets or sets the user groups that can access the product. /// /// Product User Groups - /// Customer + /// Manager, Board [Required] public IEnumerable AllowedUserGroups { get; set; } } diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs index f2bcf703..83c3d616 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs @@ -15,7 +15,8 @@ namespace CoffeeCard.Models.DataTransferObjects.v2.Product /// "NumberOfTickets": 10, /// "Name": "Espresso", /// "Description": "A coffee made by forcing steam through ground coffee beans.", - /// "Visible": false + /// "Visible": false, + /// "AllowedUserGroups": ["Manager", "Board"] /// } /// public class UpdateProductRequest @@ -76,7 +77,7 @@ public class UpdateProductRequest /// Gets or sets the user groups that can access the product. /// /// Product User Groups - /// Customer + /// Manager, Board [Required] public IEnumerable AllowedUserGroups { get; set; } } diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Products/ProductResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Products/ProductResponse.cs index f02e3403..5899e52c 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Products/ProductResponse.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Products/ProductResponse.cs @@ -14,7 +14,8 @@ namespace CoffeeCard.Models.DataTransferObjects.v2.Products /// "numberOfTickets": 10, /// "name": "Coffee clip card", /// "description": "Coffee clip card of 10 clips", - /// "isPerk": true + /// "isPerk": true, + /// "AllowedUserGroups": ["Manager", "Board"] /// } /// public class ProductResponse @@ -71,7 +72,7 @@ public class ProductResponse /// Decides the user groups that can access the product. /// /// Product User Groups - /// Customer + /// Manager, Board [Required] public IEnumerable AllowedUserGroups { get; set; } } From a018228584428235e05c6b58ef2e2bfc5e19d7f9 Mon Sep 17 00:00:00 2001 From: A-Guldborg Date: Thu, 23 Nov 2023 16:49:48 +0100 Subject: [PATCH 10/18] Remove obsolete call to get user groups of product --- coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs index fd3c00bf..7fe40488 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs @@ -119,7 +119,7 @@ public async Task UpdateProduct(UpdateProductRequest cha await _context.SaveChangesAsync(); - var existingUserGroups = _context.ProductUserGroups.Where(e => e.ProductId == changedProduct.Id); + var existingUserGroups = product.ProductUserGroup; _context.RemoveRange(existingUserGroups); From 613b776f347ad54cced62fb8bcc857cdfaab3c88 Mon Sep 17 00:00:00 2001 From: Hubert Date: Thu, 23 Nov 2023 19:14:49 +0100 Subject: [PATCH 11/18] change access to endpoint --- .../CoffeeCard.WebApi/Controllers/v2/AccountController.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs index f5971dbe..3751d641 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/AccountController.cs @@ -151,7 +151,7 @@ public async Task> EmailExists([FromBody] Emai [ProducesResponseType(typeof(ApiError), StatusCodes.Status401Unauthorized)] [ProducesResponseType(typeof(ApiError), StatusCodes.Status404NotFound)] [Route("{id:int}/user-group")] - public async Task UpdateAccountUserGroup(int id, [FromBody] UpdateUserGroupRequest updateUserGroupRequest) + public async Task UpdateAccountUserGroup([FromRoute] int id, [FromBody] UpdateUserGroupRequest updateUserGroupRequest) { await _accountService.UpdateUserGroup(updateUserGroupRequest.UserGroup, id); @@ -166,7 +166,7 @@ public async Task UpdateAccountUserGroup(int id, [FromBody] Update /// Email not found /// Account already verified [HttpPost] - [AuthorizeRoles(UserGroup.Board)] + [AllowAnonymous] [ProducesResponseType(typeof(void), StatusCodes.Status200OK)] [ProducesResponseType(typeof(ApiError), StatusCodes.Status404NotFound)] [ProducesResponseType(typeof(ApiError), StatusCodes.Status409Conflict)] From ae5239dbc915008bf3e9ed54998f7bab10eec97d Mon Sep 17 00:00:00 2001 From: A-Guldborg Date: Fri, 24 Nov 2023 09:30:57 +0100 Subject: [PATCH 12/18] Test update product user groups --- .../Services/v2/ProductServiceTest.cs | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 coffeecard/CoffeeCard.Tests.Unit/Services/v2/ProductServiceTest.cs diff --git a/coffeecard/CoffeeCard.Tests.Unit/Services/v2/ProductServiceTest.cs b/coffeecard/CoffeeCard.Tests.Unit/Services/v2/ProductServiceTest.cs new file mode 100644 index 00000000..eb688d44 --- /dev/null +++ b/coffeecard/CoffeeCard.Tests.Unit/Services/v2/ProductServiceTest.cs @@ -0,0 +1,85 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Linq; +using System.Threading.Tasks; +using CoffeeCard.Common.Configuration; +using CoffeeCard.Library.Persistence; +using CoffeeCard.Library.Services.v2; +using CoffeeCard.Models.DataTransferObjects.v2.Product; +using CoffeeCard.Models.Entities; +using Microsoft.EntityFrameworkCore; +using Xunit; + +namespace CoffeeCard.Tests.Unit.Services.v2 +{ + public class ProductServiceTest + { + [Fact(DisplayName = "UpdateProduct removes ommitted user groups and only adds selected user groups")] + public async Task UpdateProduct_Removes_Omitted_UserGroups() + { + var builder = new DbContextOptionsBuilder() + .UseInMemoryDatabase(nameof(UpdateProduct_Removes_Omitted_UserGroups)); + + var databaseSettings = new DatabaseSettings + { + SchemaName = "test" + }; + var environmentSettings = new EnvironmentSettings() + { + EnvironmentType = EnvironmentType.Test + }; + + await using var context = new CoffeeCardContext(builder.Options, databaseSettings, environmentSettings); + var p = new Product + { + Id = 1, + Name = "Coffee", + Description = "Coffee Clip card", + NumberOfTickets = 10, + Price = 10, + ExperienceWorth = 10, + Visible = true + }; + await context.AddAsync(p); + await context.SaveChangesAsync(); + + await context.AddAsync(new ProductUserGroup + { + Product = p, + UserGroup = UserGroup.Barista + }); + + await context.AddAsync(new ProductUserGroup + { + Product = p, + UserGroup = UserGroup.Manager + }); + + await context.SaveChangesAsync(); + + using var productService = new ProductService(context); + + await productService.UpdateProduct(new UpdateProductRequest() + { + Id = 1, + Visible = true, + Price = 10, + NumberOfTickets = 10, + Name = "Coffee", + Description = "Coffee Clip card", + AllowedUserGroups = new List() { UserGroup.Customer, UserGroup.Board } + }); + + var expected = new List + { + UserGroup.Customer, UserGroup.Board + }; + + var result = await productService.GetProductAsync(1); + + Assert.Collection(expected, + e => e.Equals(UserGroup.Customer), + e => e.Equals(UserGroup.Board)); + } + } +} \ No newline at end of file From 636d6aeef89a914e48fe85191115c8ff2356532f Mon Sep 17 00:00:00 2001 From: A-Guldborg Date: Mon, 27 Nov 2023 14:48:38 +0100 Subject: [PATCH 13/18] Removes second call to DB to update user groups --- .../Services/v2/ProductService.cs | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs index 7fe40488..f10f419f 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs @@ -110,26 +110,19 @@ public async Task AddProduct(AddProductRequest newProduc public async Task UpdateProduct(UpdateProductRequest changedProduct) { + var newProductUserGroups = changedProduct.AllowedUserGroups.Select(userGroup => new ProductUserGroup + { + ProductId = changedProduct.Id, + UserGroup = userGroup + }).ToList(); + var product = await GetProductAsync(changedProduct.Id); product.Price = changedProduct.Price; product.Description = changedProduct.Description; product.NumberOfTickets = changedProduct.NumberOfTickets; product.Name = changedProduct.Name; product.Visible = changedProduct.Visible; - - await _context.SaveChangesAsync(); - - var existingUserGroups = product.ProductUserGroup; - - _context.RemoveRange(existingUserGroups); - - var newProductUserGroups = changedProduct.AllowedUserGroups.Select(userGroup => new ProductUserGroup - { - ProductId = product.Id, - UserGroup = userGroup - }).ToList(); - - _context.AddRange(newProductUserGroups); + product.ProductUserGroup = newProductUserGroups; await _context.SaveChangesAsync(); From 04b44197a8233d19cfbc5a75406a4129942d835c Mon Sep 17 00:00:00 2001 From: A-Guldborg Date: Mon, 27 Nov 2023 15:11:34 +0100 Subject: [PATCH 14/18] Removes redundant database call --- .../Services/v2/ProductService.cs | 9 +--- .../Services/v2/ProductServiceTest.cs | 43 +++++++++++++++++++ 2 files changed, 45 insertions(+), 7 deletions(-) diff --git a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs index f10f419f..42edd6c0 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs @@ -81,18 +81,13 @@ public async Task AddProduct(AddProductRequest newProduc Visible = newProduct.Visible }; - _context.Products.Add(product); - await _context.SaveChangesAsync(); - - var productUserGroups = newProduct.AllowedUserGroups.Select(userGroup => new ProductUserGroup + product.ProductUserGroup = newProduct.AllowedUserGroups.Select(userGroup => new ProductUserGroup { ProductId = product.Id, UserGroup = userGroup }).ToList(); - _context.ProductUserGroups.AddRange(productUserGroups); - - + _context.Products.Add(product); await _context.SaveChangesAsync(); var result = new ChangedProductResponse diff --git a/coffeecard/CoffeeCard.Tests.Unit/Services/v2/ProductServiceTest.cs b/coffeecard/CoffeeCard.Tests.Unit/Services/v2/ProductServiceTest.cs index eb688d44..f50876ed 100644 --- a/coffeecard/CoffeeCard.Tests.Unit/Services/v2/ProductServiceTest.cs +++ b/coffeecard/CoffeeCard.Tests.Unit/Services/v2/ProductServiceTest.cs @@ -81,5 +81,48 @@ await productService.UpdateProduct(new UpdateProductRequest() e => e.Equals(UserGroup.Customer), e => e.Equals(UserGroup.Board)); } + + [Fact(DisplayName = "AddProduct adds only selected user groups")] + public async Task AddProduct_Sets_Correct_UserGroups() + { + var builder = new DbContextOptionsBuilder() + .UseInMemoryDatabase(nameof(AddProduct_Sets_Correct_UserGroups)); + + var databaseSettings = new DatabaseSettings + { + SchemaName = "test" + }; + var environmentSettings = new EnvironmentSettings() + { + EnvironmentType = EnvironmentType.Test + }; + + await using var context = new CoffeeCardContext(builder.Options, databaseSettings, environmentSettings); + + using var productService = new ProductService(context); + + var p = new AddProductRequest + { + Name = "Coffee", + Description = "Coffee Clip card", + NumberOfTickets = 10, + Price = 10, + Visible = true, + AllowedUserGroups = new List { UserGroup.Manager, UserGroup.Board } + }; + + await productService.AddProduct(p); + + var expected = new List + { + UserGroup.Manager, UserGroup.Board + }; + + var result = await productService.GetProductAsync(1); + + Assert.Collection(expected, + e => e.Equals(UserGroup.Customer), + e => e.Equals(UserGroup.Board)); + } } } \ No newline at end of file From 071d3fab8b6f7ef04cefd4a4410f128396f714b9 Mon Sep 17 00:00:00 2001 From: A-Guldborg Date: Mon, 27 Nov 2023 23:25:16 +0100 Subject: [PATCH 15/18] Small fixes to decrease unnecessary code --- .../Services/v2/ProductService.cs | 24 ++++++++----------- 1 file changed, 10 insertions(+), 14 deletions(-) diff --git a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs index 42edd6c0..e1e471ce 100644 --- a/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs +++ b/coffeecard/CoffeeCard.Library/Services/v2/ProductService.cs @@ -78,15 +78,13 @@ public async Task AddProduct(AddProductRequest newProduc Name = newProduct.Name, NumberOfTickets = newProduct.NumberOfTickets, ExperienceWorth = 0, - Visible = newProduct.Visible + Visible = newProduct.Visible, + ProductUserGroup = newProduct.AllowedUserGroups.Select(userGroup => new ProductUserGroup + { + UserGroup = userGroup + }).ToList() }; - product.ProductUserGroup = newProduct.AllowedUserGroups.Select(userGroup => new ProductUserGroup - { - ProductId = product.Id, - UserGroup = userGroup - }).ToList(); - _context.Products.Add(product); await _context.SaveChangesAsync(); @@ -105,19 +103,17 @@ public async Task AddProduct(AddProductRequest newProduc public async Task UpdateProduct(UpdateProductRequest changedProduct) { - var newProductUserGroups = changedProduct.AllowedUserGroups.Select(userGroup => new ProductUserGroup - { - ProductId = changedProduct.Id, - UserGroup = userGroup - }).ToList(); - var product = await GetProductAsync(changedProduct.Id); product.Price = changedProduct.Price; product.Description = changedProduct.Description; product.NumberOfTickets = changedProduct.NumberOfTickets; product.Name = changedProduct.Name; product.Visible = changedProduct.Visible; - product.ProductUserGroup = newProductUserGroups; + product.ProductUserGroup = changedProduct.AllowedUserGroups.Select(userGroup => new ProductUserGroup + { + ProductId = changedProduct.Id, + UserGroup = userGroup + }).ToList(); await _context.SaveChangesAsync(); From e9fb98b66bb66131e5a7829a31c558b0c2ff7cb4 Mon Sep 17 00:00:00 2001 From: A-Guldborg Date: Tue, 28 Nov 2023 17:31:15 +0100 Subject: [PATCH 16/18] Remove code smells --- .../DataTransferObjects/v2/Product/AddProductRequest.cs | 2 +- .../DataTransferObjects/v2/Product/ChangedProductResponse.cs | 2 +- .../DataTransferObjects/v2/Product/UpdateProductRequest.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs index e9593220..148b75ac 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/AddProductRequest.cs @@ -70,7 +70,7 @@ public class AddProductRequest /// Product User Groups /// Manager, Board [Required] - public IEnumerable AllowedUserGroups { get; set; } + public IEnumerable AllowedUserGroups { get; set; } = new List(); } } \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ChangedProductResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ChangedProductResponse.cs index 9e2f2011..2fac818a 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ChangedProductResponse.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/ChangedProductResponse.cs @@ -59,6 +59,6 @@ public class ChangedProductResponse /// Product User Groups /// Manager, Board [Required] - public IEnumerable AllowedUserGroups { get; set; } + public IEnumerable AllowedUserGroups { get; set; } = new List(); } } \ No newline at end of file diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs index 83c3d616..e0aa10b2 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Product/UpdateProductRequest.cs @@ -79,6 +79,6 @@ public class UpdateProductRequest /// Product User Groups /// Manager, Board [Required] - public IEnumerable AllowedUserGroups { get; set; } + public IEnumerable AllowedUserGroups { get; set; } = new List(); } } \ No newline at end of file From abaa29464788360692c4678e53a6586f6598d521 Mon Sep 17 00:00:00 2001 From: A-Guldborg Date: Tue, 28 Nov 2023 17:36:29 +0100 Subject: [PATCH 17/18] Fix last code smell --- .../DataTransferObjects/v2/Products/ProductResponse.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Products/ProductResponse.cs b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Products/ProductResponse.cs index 5899e52c..959cd2b0 100644 --- a/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Products/ProductResponse.cs +++ b/coffeecard/CoffeeCard.Models/DataTransferObjects/v2/Products/ProductResponse.cs @@ -74,6 +74,6 @@ public class ProductResponse /// Product User Groups /// Manager, Board [Required] - public IEnumerable AllowedUserGroups { get; set; } + public IEnumerable AllowedUserGroups { get; set; } = new List(); } } From 0789c28640d1c730ebc73beb30be42af0c00fad7 Mon Sep 17 00:00:00 2001 From: A-Guldborg <95026056+A-Guldborg@users.noreply.github.com> Date: Thu, 30 Nov 2023 19:26:47 +0100 Subject: [PATCH 18/18] Add productusergroups to get products (#231) --- .../CoffeeCard.WebApi/Controllers/v2/ProductsController.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs index 36bed38f..1e1fbe7e 100644 --- a/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs +++ b/coffeecard/CoffeeCard.WebApi/Controllers/v2/ProductsController.cs @@ -101,7 +101,8 @@ private static ProductResponse MapProductToDto(Product product) Description = product.Description, NumberOfTickets = product.NumberOfTickets, Price = product.Price, - IsPerk = product.IsPerk() + IsPerk = product.IsPerk(), + AllowedUserGroups = product.ProductUserGroup.Select(e => e.UserGroup) }; } }