Add user manager page #22

Merged
Egamorf merged 11 commits from feature/user-manager into main 2025-04-27 20:49:57 +02:00
12 changed files with 208 additions and 10 deletions
Showing only changes of commit 63622bd299 - Show all commits

View File

@@ -7,4 +7,7 @@ public interface IUserGateway
{ {
Task<UserListDto> GetUsers(UserFilterParams filterParams, int currentPage); Task<UserListDto> GetUsers(UserFilterParams filterParams, int currentPage);
Task<IEnumerable<RoleDto>> GetRoles(); Task<IEnumerable<RoleDto>> GetRoles();
Task<string> CreateUser(UserDto user);
Task<string> UpdateUser(UserDto user);
Task<string> DeleteUser(string userId);
} }

View File

@@ -9,6 +9,32 @@ namespace GameIdeas.BlazorApp.Pages.Users.Gateways;
public class UserGateway(IHttpClientService httpClient) : IUserGateway public class UserGateway(IHttpClientService httpClient) : IUserGateway
{ {
public async Task<string> CreateUser(UserDto user)
{
try
{
return await httpClient.PostAsync<string>(Endpoints.User.Create, user)
?? throw new InvalidOperationException(ResourcesKey.ErrorCreateUser);
}
catch (Exception)
{
throw new UserCreationException(ResourcesKey.ErrorCreateUser);
}
}
public async Task<string> DeleteUser(string userId)
{
try
{
return await httpClient.DeleteAsync<string>(Endpoints.User.Delete(userId))
?? throw new InvalidOperationException(ResourcesKey.ErrorDeleteUser);
}
catch (Exception)
{
throw new UserCreationException(ResourcesKey.ErrorDeleteUser);
}
}
public async Task<IEnumerable<RoleDto>> GetRoles() public async Task<IEnumerable<RoleDto>> GetRoles()
{ {
try try
@@ -42,4 +68,17 @@ public class UserGateway(IHttpClientService httpClient) : IUserGateway
throw new UserNotFoundException(ResourcesKey.ErrorFetchUsers); throw new UserNotFoundException(ResourcesKey.ErrorFetchUsers);
} }
} }
public async Task<string> UpdateUser(UserDto user)
{
try
{
return await httpClient.PutAsync<string>(Endpoints.User.Update(user.Id ?? string.Empty), user)
?? throw new InvalidOperationException(ResourcesKey.ErrorUpdateUser);
}
catch (Exception)
{
throw new UserCreationException(ResourcesKey.ErrorUpdateUser);
}
}
} }

View File

@@ -25,5 +25,9 @@ public static class Endpoints
{ {
public static string Fetch(UserFilterDto filter) => $"api/User?{UrlHelper.BuildUrlParams(filter)}"; public static string Fetch(UserFilterDto filter) => $"api/User?{UrlHelper.BuildUrlParams(filter)}";
public const string Roles = "api/User/Roles"; public const string Roles = "api/User/Roles";
public const string Create = "api/User/Create";
public static string Delete(string userId) => $"api/User/Delete/{userId}";
public static string Update(string userId) => $"api/User/Update/{userId}";
} }
} }

View File

@@ -0,0 +1,3 @@
namespace GameIdeas.BlazorApp.Shared.Exceptions;
public class UserCreationException(string message) : Exception(message);

View File

@@ -58,6 +58,9 @@ public class Translations (TranslationService translationService)
public string ErrorFetchUsers => translationService.Translate(nameof(ErrorFetchUsers)); public string ErrorFetchUsers => translationService.Translate(nameof(ErrorFetchUsers));
public string ErrorFetchRoles => translationService.Translate(nameof(ErrorFetchRoles)); public string ErrorFetchRoles => translationService.Translate(nameof(ErrorFetchRoles));
public string MissingField => translationService.Translate(nameof(MissingField)); public string MissingField => translationService.Translate(nameof(MissingField));
public string ErrorCreateUser => translationService.Translate(nameof(ErrorCreateUser));
public string ErrorUpdateUser => translationService.Translate(nameof(ErrorUpdateUser));
public string ErrorDeleteUser => translationService.Translate(nameof(ErrorDeleteUser));
} }
public static class ResourcesKey public static class ResourcesKey
@@ -124,4 +127,7 @@ public static class ResourcesKey
public static string ErrorFetchUsers => _instance?.ErrorFetchUsers ?? throw new InvalidOperationException("ResourcesKey.ErrorFetchUsers is not initialized."); public static string ErrorFetchUsers => _instance?.ErrorFetchUsers ?? throw new InvalidOperationException("ResourcesKey.ErrorFetchUsers is not initialized.");
public static string ErrorFetchRoles => _instance?.ErrorFetchRoles ?? throw new InvalidOperationException("ResourcesKey.ErrorFetchRoles is not initialized."); public static string ErrorFetchRoles => _instance?.ErrorFetchRoles ?? throw new InvalidOperationException("ResourcesKey.ErrorFetchRoles is not initialized.");
public static string MissingField => _instance?.MissingField ?? throw new InvalidOperationException("ResourcesKey.MissingField is not initialized."); public static string MissingField => _instance?.MissingField ?? throw new InvalidOperationException("ResourcesKey.MissingField is not initialized.");
public static string ErrorCreateUser => _instance?.ErrorCreateUser ?? throw new InvalidOperationException("ResourcesKey.ErrorCreateUser is not initialized.");
public static string ErrorUpdateUser => _instance?.ErrorUpdateUser ?? throw new InvalidOperationException("ResourcesKey.ErrorUpdateUser is not initialized.");
public static string ErrorDeleteUser => _instance?.ErrorDeleteUser ?? throw new InvalidOperationException("ResourcesKey.ErrorDeleteUser is not initialized.");
} }

View File

@@ -10,7 +10,8 @@ namespace GameIdeas.WebAPI.Controllers;
[ApiController] [ApiController]
[Route("api/[controller]")] [Route("api/[controller]")]
public class UserController( public class UserController(
IUserService userService, IUserReadService userReadService,
IUserWriteService userWriteService,
ILoggerFactory loggerFactory) : Controller ILoggerFactory loggerFactory) : Controller
{ {
private readonly ILogger<UserController> logger = loggerFactory.CreateLogger<UserController>(); private readonly ILogger<UserController> logger = loggerFactory.CreateLogger<UserController>();
@@ -20,7 +21,7 @@ public class UserController(
{ {
try try
{ {
return Ok(await userService.Login(model)); return Ok(await userReadService.Login(model));
} }
catch (UserInvalidException e) catch (UserInvalidException e)
{ {
@@ -45,7 +46,7 @@ public class UserController(
{ {
try try
{ {
return Ok(await userService.GetRoles()); return Ok(await userReadService.GetRoles());
} }
catch (Exception e) catch (Exception e)
{ {
@@ -60,7 +61,7 @@ public class UserController(
{ {
try try
{ {
return Ok(await userService.GetUsers(filter)); return Ok(await userReadService.GetUsers(filter));
} }
catch (Exception e) catch (Exception e)
{ {
@@ -68,4 +69,49 @@ public class UserController(
return StatusCode(500, e.Message); return StatusCode(500, e.Message);
} }
} }
[Authorize(Roles = GlobalConstants.ADMINISTRATOR)]
[HttpPost("Create")]
public async Task<ActionResult<string>> CreateUser([FromBody] UserDto user)
{
try
{
return Created("/Create", await userWriteService.CreateUser(user));
}
catch (Exception e)
{
logger.LogError(e, "Internal error while create user");
return StatusCode(500, e.Message);
}
}
[Authorize(Roles = GlobalConstants.ADMINISTRATOR)]
[HttpPut("Update/{userId}")]
public async Task<ActionResult<string>> UpdateUser(string userId, [FromBody] UserDto user)
{
try
{
return Created("/Update", await userWriteService.UpdateUser(userId, user));
}
catch (Exception e)
{
logger.LogError(e, "Internal error while update user");
return StatusCode(500, e.Message);
}
}
[Authorize(Roles = GlobalConstants.ADMINISTRATOR)]
[HttpDelete("Delete/{userId}")]
public async Task<ActionResult<string>> DeleteUser(string userId)
{
try
{
return Created("/Delete", await userWriteService.DeleteUser(userId));
}
catch (Exception e)
{
logger.LogError(e, "Internal error while delete user");
return StatusCode(500, e.Message);
}
}
} }

View File

@@ -34,7 +34,7 @@
"RequestFailedStatusFormat": "Erreur lors de la réponse, code {0}", "RequestFailedStatusFormat": "Erreur lors de la réponse, code {0}",
"ErrorFetchCategories": "Erreur lors de la récupération des catégories", "ErrorFetchCategories": "Erreur lors de la récupération des catégories",
"PlaceholderAdd": "Ajouter un nouveau", "PlaceholderAdd": "Ajouter un nouveau",
"ErrorCreateGame": "Erreur lors de la Création d'un jeu", "ErrorCreateGame": "Erreur lors de la création d'un jeu",
"InvalidTitle": "Le titre est incorrect", "InvalidTitle": "Le titre est incorrect",
"InvalidInterest": "L'interêt est incorrect", "InvalidInterest": "L'interêt est incorrect",
"Unknown": "Inconnu", "Unknown": "Inconnu",
@@ -53,5 +53,8 @@
"Roles": "Rôles", "Roles": "Rôles",
"ErrorFetchUsers": "Erreur lors de la récupération des utilisateurs", "ErrorFetchUsers": "Erreur lors de la récupération des utilisateurs",
"ErrorFetchRoles": "Erreur lors de la récupération des rôles", "ErrorFetchRoles": "Erreur lors de la récupération des rôles",
"MissingField": "Un champs est manquant" "MissingField": "Un champs est manquant",
"ErrorCreateUser": "Erreur lors de la création d'un utilisateur",
"ErrorUpdateUser": "Erreur lors de la mise à jour d'un utilisateur",
"ErrorDeleteUser": "Erreur lors de la suppression d'un utilisateur"
} }

View File

@@ -64,7 +64,8 @@ services.AddAuthorization();
services.AddSingleton<TranslationService>(); services.AddSingleton<TranslationService>();
services.AddSingleton<Translations>(); services.AddSingleton<Translations>();
services.AddScoped<IUserService, UserService>(); services.AddScoped<IUserReadService, UserReadService>();
services.AddScoped<IUserWriteService, UserWriteService>();
services.AddScoped<IGameReadService, GameReadService>(); services.AddScoped<IGameReadService, GameReadService>();
services.AddScoped<IGameWriteService, GameWriteService>(); services.AddScoped<IGameWriteService, GameWriteService>();
services.AddScoped<ICategoryService, CategoryService>(); services.AddScoped<ICategoryService, CategoryService>();

View File

@@ -2,7 +2,7 @@
namespace GameIdeas.WebAPI.Services.Users; namespace GameIdeas.WebAPI.Services.Users;
public interface IUserService public interface IUserReadService
{ {
Task<TokenDto> Login(UserDto user); Task<TokenDto> Login(UserDto user);
Task<IEnumerable<RoleDto>> GetRoles(); Task<IEnumerable<RoleDto>> GetRoles();

View File

@@ -0,0 +1,10 @@
using GameIdeas.Shared.Dto;
namespace GameIdeas.WebAPI.Services.Users;
public interface IUserWriteService
{
Task<string> CreateUser(UserDto user);
Task<string> UpdateUser(string userId, UserDto user);
Task<string> DeleteUser(string userId);
}

View File

@@ -14,10 +14,10 @@ using System.Text;
namespace GameIdeas.WebAPI.Services.Users; namespace GameIdeas.WebAPI.Services.Users;
public class UserService( public class UserReadService(
UserManager<User> userManager, UserManager<User> userManager,
GameIdeasContext context, GameIdeasContext context,
IMapper mapper) : IUserService IMapper mapper) : IUserReadService
{ {
public async Task<IEnumerable<RoleDto>> GetRoles() public async Task<IEnumerable<RoleDto>> GetRoles()
{ {

View File

@@ -0,0 +1,83 @@
using AutoMapper;
using GameIdeas.Resources;
using GameIdeas.Shared.Dto;
using GameIdeas.Shared.Model;
using GameIdeas.WebAPI.Exceptions;
using Microsoft.AspNetCore.Identity;
namespace GameIdeas.WebAPI.Services.Users;
public class UserWriteService(
UserManager<User> userManager) : IUserWriteService
{
public async Task<string> CreateUser(UserDto user)
{
if (user.Username == null ||
user.Password == null ||
user.Role == null)
{
throw new UserInvalidException(ResourcesKey.MissingField);
}
User userToCreate = new() { UserName = user.Username };
var result = await userManager.CreateAsync(userToCreate, user.Password);
if (result.Succeeded)
{
await userManager.AddToRoleAsync(userToCreate, user.Role.Name);
}
else
{
throw new UserInvalidException(string.Join("; ", result.Errors));
}
return userToCreate.Id;
}
public async Task<string> DeleteUser(string userId)
{
if (userId == null)
{
throw new ArgumentNullException(ResourcesKey.MissingField);
}
var user = await userManager.FindByIdAsync(userId)
?? throw new UserInvalidException("User not found");
await userManager.DeleteAsync(user);
return userId;
}
public async Task<string> UpdateUser(string userId, UserDto user)
{
if (userId == null)
{
throw new ArgumentNullException(ResourcesKey.MissingField);
}
var userToUpdate = await userManager.FindByIdAsync(userId)
?? throw new UserInvalidException("User not found");
if (user.Username != null)
{
userToUpdate.UserName = user.Username;
await userManager.UpdateAsync(userToUpdate);
}
if (user.Password != null)
{
await userManager.RemovePasswordAsync(userToUpdate);
await userManager.AddPasswordAsync(userToUpdate, user.Password);
}
if (user.Role != null)
{
var roles = await userManager.GetRolesAsync(userToUpdate);
await userManager.RemoveFromRolesAsync(userToUpdate, roles);
await userManager.AddToRoleAsync(userToUpdate, user.Role.Name);
}
return userToUpdate.Id;
}
}