using System.ComponentModel.DataAnnotations; using APIMappers; using Dto; using HeartTrackAPI.Request; using HeartTrackAPI.Responce; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Model.Manager; using Model.Repository; using Shared; namespace HeartTrackAPI.Controllers; /// /// Contrôle les actions liées aux utilisateurs dans l'application HeartTrack. /// Gère les opérations CRUD sur les utilisateurs, leurs amis, et leurs activités. /// [ApiController] [ApiVersion("1.0")] [Route("api/v{version:apiVersion}/[controller]")] public class UsersController : Controller { private readonly ILogger _logger; private readonly IActivityRepository _activityService; private readonly IUserRepository _userService; public UsersController(ILogger logger, IDataManager dataManager) { _logger = logger; _userService = dataManager.UserRepo; _activityService = dataManager.ActivityRepo; } /// /// Récupère une page d'utilisateurs en fonction des critères de pagination et de tri fournis. /// /// Les critères de pagination et de tri pour les utilisateurs. /// Une page de données utilisateur selon les critères spécifiés. /// Retourne la page demandée d'utilisateurs. /// La demande de pagination est invalide. /// Erreur interne du serveur. [HttpGet] [ProducesResponseType(typeof(PageResponse), 200)] [ProducesResponseType(400)] [ProducesResponseType(500)] public async Task>> Get([FromQuery] PageRequest request) { try { var totalCount = await _userService.GetNbItems(); if (request.Count * request.Index >= totalCount) { _logger.LogError("To many object is asked the max is {totalCount} but the request is superior of ", totalCount); return BadRequest("To many object is asked the max is : " + totalCount); } _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(Get), null); var athletes = await _userService.GetUsers(request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : AthleteOrderCriteria.None, request.Descending ?? false); var pageResponse = new PageResponse(request.Index, request.Count, totalCount, athletes!.Select(a => a.ToDto())); return Ok(pageResponse); } catch (Exception e) { _logger.LogError(e, "Error while getting all athletes"); return Problem(); } } /// /// Récupère un utilisateur spécifique par son identifiant. /// /// L'identifiant de l'utilisateur à récupérer. /// L'utilisateur correspondant à l'identifiant spécifié. /// Retourne l'utilisateur demandé. /// Aucun utilisateur trouvé pour l'identifiant spécifié. /// Erreur interne du serveur. [HttpGet("{id}")] [ProducesResponseType(typeof(UserDto), 200)] [ProducesResponseType(404)] [ProducesResponseType(500)] public async Task> GetById([Range(0,int.MaxValue)]int id) { try { _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetById), id); var athlete = await _userService.GetItemById(id); if (athlete == null) { _logger.LogError("Athlete with id {id} not found", id); return NotFound($"Athlete with id {id} not found"); } return Ok(athlete.ToDto()); } catch (Exception e) { _logger.LogError(e, "Error while getting athlete by id {id}", id); return Problem(); } } /// /// Obtient le nombre total d'utilisateurs. /// /// Le nombre total d'utilisateurs. /// Retourne le nombre total d'utilisateurs. /// Erreur interne du serveur. [HttpGet("count")] [ProducesResponseType(typeof(int), 200)] [ProducesResponseType(500)] public async Task> Count() { try { _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(Count), null); var nbUsers = await _userService.GetNbItems(); return Ok(nbUsers); } catch (Exception e) { _logger.LogError(e, "Error while getting the number of users"); return Problem(); } } /// /// Met à jour les informations d'un utilisateur spécifique. /// /// L'identifiant de l'utilisateur à mettre à jour. /// Les données de l'utilisateur pour la mise à jour. /// L'utilisateur mis à jour. /// Retourne l'utilisateur mis à jour. /// Utilisateur non trouvé. /// Erreur interne du serveur. [HttpPut("{id}")] [ProducesResponseType(typeof(UserDto), 200)] [ProducesResponseType(404)] [ProducesResponseType(500)] public async Task> Update(int id, [FromBody] UserDto user) { try { _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(Update), user,id); var athlete = await _userService.GetItemById(id); if (athlete == null) { _logger.LogError("Athlete with id {id} not found", id); return NotFound($"Athlete with id {id} not found"); } var updatedAthlete = await _userService.UpdateItem(id, user.ToModel()); if(updatedAthlete == null) { _logger.LogError("Error while updating athlete with id {id}", id); return Problem(); } return Ok(updatedAthlete.ToDto()); } catch (Exception e) { _logger.LogError(e, "Error while getting the number of users"); return Problem(); } } /// /// Supprime un utilisateur spécifique. /// /// L'identifiant de l'utilisateur à supprimer. /// Action result. /// Utilisateur supprimé avec succès. /// Utilisateur non trouvé. /// Erreur interne du serveur. [HttpDelete("{id}")] [ProducesResponseType(200)] [ProducesResponseType(404)] [ProducesResponseType(500)] public async Task Delete(int id) { try { _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(Delete), null,id); var athlete = await _userService.GetItemById(id); if (athlete == null) { _logger.LogError("Athlete with id {id} not found", id); return NotFound($"Athlete with id {id} not found"); } var isDeleted = await _userService.DeleteItem(id); if(!isDeleted) { _logger.LogError("Error while deleting athlete with id {id}", id); return Problem(); } return Ok(); } catch (Exception e) { _logger.LogError(e, "Error while getting the number of users"); return Problem(); } } /// /// Obtient la liste des amis d'un utilisateur spécifique. /// /// L'identifiant de l'utilisateur. /// Les critères de pagination et de tri. /// La liste paginée des amis. /// Retourne la liste paginée des amis de l'utilisateur. /// Utilisateur non trouvé. /// Erreur interne du serveur. [HttpGet("{id}/friends")] [ProducesResponseType(typeof(PageResponse), 200)] [ProducesResponseType(404)] [ProducesResponseType(500)] public async Task>> GetFriends(int id, [FromQuery] PageRequest request) { try { _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(GetFriends), null,id); var athlete = await _userService.GetItemById(id); if (athlete == null) { _logger.LogError("Athlete with id {id} not found", id); return NotFound($"Athlete with id {id} not found"); } var totalCount = await _userService.GetNbFriends(athlete); if (request.Count * request.Index >= totalCount) { _logger.LogError("To many object is asked the max is {totalCount} but the request is superior of ", totalCount); return BadRequest("To many object is asked the max is : " + totalCount); } var friends = await _userService.GetFriends(athlete, request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : AthleteOrderCriteria.None, request.Descending ?? false); if (friends == null) return NotFound(); var pageResponse = new PageResponse(request.Index, request.Count, totalCount, friends.Select(a => a.ToDto())); return Ok(pageResponse); } catch (Exception e) { _logger.LogError(e, "Error while getting the number of users"); return Problem(); } } /// /// Ajoute un ami à un utilisateur spécifique. /// /// L'identifiant de l'utilisateur. /// L'identifiant de l'ami à ajouter. /// Action result. /// Ami ajouté avec succès. /// Utilisateur ou ami non trouvé. /// Erreur interne du serveur. [HttpPost("{id}/friend/{friendId}")] [ProducesResponseType(200)] [ProducesResponseType(404)] [ProducesResponseType(500)] public async Task AddFriend(int id, int friendId) { try { _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(AddFriend), friendId,id); var athlete = await _userService.GetItemById(id); if (athlete == null) { _logger.LogError("Athlete with id {id} not found", id); return NotFound($"Athlete with id {id} not found"); } var friend = await _userService.GetItemById(friendId); if (friend == null) { _logger.LogError("Athlete with id {id} not found", friendId); return NotFound($"Athlete with id {friendId} not found"); } var isAdded = await _userService.AddFriend(athlete, friend); if(!isAdded) { _logger.LogError("Error while adding friend with id {friendId} to athlete with id {id}", friendId, id); return Problem(); } return Ok(); } catch (Exception e) { _logger.LogError(e, "Error while getting the number of users"); return Problem(); } } /// /// Supprime un ami d'un utilisateur spécifique. /// /// L'identifiant de l'utilisateur. /// L'identifiant de l'ami à supprimer. /// Action result. /// Ami supprimé avec succès. /// Utilisateur ou ami non trouvé. /// Erreur interne du serveur. [HttpDelete("{id}/friend/{friendId}")] [ProducesResponseType(200)] [ProducesResponseType(404)] [ProducesResponseType(500)] public async Task RemoveFriend(int id, int friendId) { try { _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(RemoveFriend), friendId,id); var athlete = await _userService.GetItemById(id); if (athlete == null) { _logger.LogError("Athlete with id {id} not found", id); return NotFound($"Athlete with id {id} not found"); } var friend = await _userService.GetItemById(friendId); if (friend == null) { _logger.LogError("Athlete with id {id} not found", friendId); return NotFound($"Athlete with id {friendId} not found"); } var isRemoved = await _userService.RemoveFriend(athlete, friend); if(!isRemoved) { _logger.LogError("Error while removing friend with id {friendId} to athlete with id {id}", friendId, id); return Problem(); } return Ok(); } catch (Exception e) { _logger.LogError(e, "Error while getting the number of users"); return Problem(); } } // #[TODO] [Dave] ou faire un get qui si le role est coach resend les athletes et si le role est athlete resend les coach /// /// Obtient la liste des athlètes d'un coach spécifique. /// /// L'identifiant du coach. /// Les critères de pagination et de tri. /// La liste paginée des athlètes. /// Retourne la liste paginée des athlètes du coach. /// Coach non trouvé. /// Erreur interne du serveur. [HttpGet("{coachId}/athletes")] [ProducesResponseType(typeof(PageResponse), 200)] [ProducesResponseType(404)] [ProducesResponseType(500)] public async Task>> GetAthletes(int coachId, [FromQuery] PageRequest request) { try { _logger.LogInformation("Executing {Action} with parameters: {Parameters} for {Id}", nameof(GetAthletes), null,coachId); var coach = await _userService.GetItemById(coachId); if (coach == null) { _logger.LogError("Athlete with id {id} not found", coachId); return NotFound($"Athlete with id {coachId} not found"); } var totalCount = await _userService.GetNbFriends(coach); if (request.Count * request.Index >= totalCount) { _logger.LogError("To many object is asked the max is {totalCount} but the request is superior of ", totalCount); return BadRequest("To many object is asked the max is : " + totalCount); } var athletes = await _userService.GetFriends(coach, request.Index, request.Count, Enum.TryParse(request.OrderingPropertyName, out AthleteOrderCriteria result) ? result : AthleteOrderCriteria.None, request.Descending ?? false); if (athletes == null) return NotFound(); var pageResponse = new PageResponse(request.Index, request.Count, totalCount, athletes.Select(a => a.ToDto())); return Ok(pageResponse); } catch (Exception e) { _logger.LogError(e, "Error while getting the number of users"); return Problem(); } } /// /// Obtient la liste des activités d'un utilisateur spécifique. /// /// L'identifiant de l'utilisateur. /// Les critères de pagination et de tri. /// La liste paginée des activités de l'utilisateur. /// Retourne la liste paginée des activités. /// Aucune activité trouvée. /// Erreur interne du serveur. [HttpGet("{userId}/activities")] // should be tiny DTOActivity returned with only the necessary information (will be used in the list of activities of a user) public async Task>> GetActivitiesByUser(int userId, [FromQuery] PageRequest pageRequest) { try { var totalCount = await _activityService.GetNbActivitiesByUser(userId); if (pageRequest.Count * pageRequest.Index >= totalCount) { _logger.LogError("To many object is asked the max is {totalCount} but the request is superior of ", totalCount); return BadRequest("To many object is asked the max is : " + totalCount); } _logger.LogInformation("Executing {Action} with parameters: {Parameters}", nameof(GetActivitiesByUser), pageRequest); var activities = await _activityService.GetActivitiesByUser(userId, pageRequest.Index, pageRequest.Count, ActivityOrderCriteria.None, pageRequest.Descending ?? false); if(activities == null) { return NotFound("No activities found"); } var pageResponse = new PageResponse(pageRequest.Index, pageRequest.Count, totalCount, activities.Select(a => a.ToDto())); return Ok(pageResponse); } catch (Exception e) { _logger.LogError(e, "Error while getting all activities"); return Problem(); } } /* [TODO] [Dave] [HttpGet("{userId}/trainings")] [ProducesResponseType(typeof(PageResponse), 200)] [ProducesResponseType(404)] [ProducesResponseType(500)] public async Task> GetTrainings(int userId, [FromQuery] PageRequest request) */ /// /// Déconnecte l'utilisateur actuel. /// /// Le gestionnaire de connexion. /// Paramètre vide utilisé pour s'assurer que la requête provient bien d'un client. /// Action result. /// Déconnexion réussie. /// Déconnexion non autorisée. /// Erreur interne du serveur. [HttpPost("logout")] [ProducesResponseType(200)] [ProducesResponseType(401)] [ProducesResponseType(500)] public async Task Logout(SignInManager signInManager, [FromBody] object? empty) { if (empty == null) return Unauthorized(); await signInManager.SignOutAsync(); return Ok(); } }