From f1ef5302c10c6b95b03c8dbbe9679d85092871b3 Mon Sep 17 00:00:00 2001 From: sanchezvem Date: Sun, 22 Feb 2026 12:02:44 +0100 Subject: [PATCH] Created endpoints to manage friends --- BeReadyBackend/BeReadyBackend.csproj | 1 - BeReadyBackend/DTO/Friends/GetFriendDto.cs | 7 +++ .../DTO/Friends/GetFriendRequestDto.cs | 7 +++ .../GetLockedAchievementsEndpoint.cs | 13 +----- .../GetUserAchievementsEndpoint.cs | 11 ----- .../Achievements/UnlockAchievementEndpoint.cs | 5 +-- .../Friends/AcceptFriendRequestEndpoint.cs | 45 +++++++++++++++++++ .../Endpoints/Friends/DeleteFriendEndpoint.cs | 37 +++++++++++++++ .../Friends/GetAllFriendRequestsEndpoint.cs | 22 +++++++++ .../Friends/GetAllFriendsEndpoint.cs | 23 ++++++++++ .../Friends/RejectFriendRequestEndpoint.cs | 35 +++++++++++++++ .../Friends/SendFriendRequestEndpoint.cs | 39 ++++++++++++++++ .../Endpoints/Users/DeleteUserEndpoint.cs | 13 +----- .../Endpoints/Users/GetUserDetailsEndpoint.cs | 6 --- .../MappingProfiles/EntityToDtoMappings.cs | 9 ++++ .../Friends/GetFriendByCriteriaSpec.cs | 13 ++++++ .../Friends/GetFriendRequestByIdsSpec.cs | 13 ++++++ .../Friends/GetFriendRequestSpec.cs | 14 ++++++ .../Friends/GetFriendsByUserIdSpec.cs | 15 +++++++ 19 files changed, 283 insertions(+), 45 deletions(-) create mode 100644 BeReadyBackend/DTO/Friends/GetFriendDto.cs create mode 100644 BeReadyBackend/DTO/Friends/GetFriendRequestDto.cs create mode 100644 BeReadyBackend/Endpoints/Friends/AcceptFriendRequestEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Friends/DeleteFriendEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Friends/GetAllFriendRequestsEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Friends/GetAllFriendsEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Friends/RejectFriendRequestEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Friends/SendFriendRequestEndpoint.cs create mode 100644 BeReadyBackend/Specifications/Friends/GetFriendByCriteriaSpec.cs create mode 100644 BeReadyBackend/Specifications/Friends/GetFriendRequestByIdsSpec.cs create mode 100644 BeReadyBackend/Specifications/Friends/GetFriendRequestSpec.cs create mode 100644 BeReadyBackend/Specifications/Friends/GetFriendsByUserIdSpec.cs diff --git a/BeReadyBackend/BeReadyBackend.csproj b/BeReadyBackend/BeReadyBackend.csproj index 21cffe3..61caa40 100644 --- a/BeReadyBackend/BeReadyBackend.csproj +++ b/BeReadyBackend/BeReadyBackend.csproj @@ -27,7 +27,6 @@ - diff --git a/BeReadyBackend/DTO/Friends/GetFriendDto.cs b/BeReadyBackend/DTO/Friends/GetFriendDto.cs new file mode 100644 index 0000000..a143175 --- /dev/null +++ b/BeReadyBackend/DTO/Friends/GetFriendDto.cs @@ -0,0 +1,7 @@ +namespace BeReadyBackend.DTO.Friends; + +public class GetFriendDto +{ + public string? Username { get; set; } + public int Score { get; set; } +} \ No newline at end of file diff --git a/BeReadyBackend/DTO/Friends/GetFriendRequestDto.cs b/BeReadyBackend/DTO/Friends/GetFriendRequestDto.cs new file mode 100644 index 0000000..98ec0b9 --- /dev/null +++ b/BeReadyBackend/DTO/Friends/GetFriendRequestDto.cs @@ -0,0 +1,7 @@ +namespace BeReadyBackend.DTO.Friends; + +public class GetFriendRequestDto +{ + public string? Username { get; set; } + public int Score { get; set; } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Achievements/GetLockedAchievementsEndpoint.cs b/BeReadyBackend/Endpoints/Achievements/GetLockedAchievementsEndpoint.cs index cd59bef..13d6973 100644 --- a/BeReadyBackend/Endpoints/Achievements/GetLockedAchievementsEndpoint.cs +++ b/BeReadyBackend/Endpoints/Achievements/GetLockedAchievementsEndpoint.cs @@ -1,15 +1,12 @@ using BeReadyBackend.DTO.Achievements; -using BeReadyBackend.Models; using BeReadyBackend.Repositories; using BeReadyBackend.Services; using BeReadyBackend.Specifications.Achievements; -using BeReadyBackend.Specifications.Users; using FastEndpoints; namespace BeReadyBackend.Endpoints.Achievements; public class GetLockedAchievementsEndpoint( - UsersRepository usersRepository, AchievementsRepository achievementsRepository, UserService userService) : EndpointWithoutRequest> @@ -18,19 +15,11 @@ public class GetLockedAchievementsEndpoint( { Get("/Achievements/Locked/Users/"); } - + public override async Task HandleAsync(CancellationToken ct) { int userId = userService.GetUserIdFromToken(); - User? user = await usersRepository.SingleOrDefaultAsync(new GetUserByIdSpec(userId), ct); - - if (user is null) - { - await Send.NotFoundAsync(ct); - return; - } - List achievementsLocked = await achievementsRepository.ProjectToListAsync(new GetLockedAchievementsSpec(userId), ct); await Send.OkAsync(achievementsLocked, ct); diff --git a/BeReadyBackend/Endpoints/Achievements/GetUserAchievementsEndpoint.cs b/BeReadyBackend/Endpoints/Achievements/GetUserAchievementsEndpoint.cs index fafdbb3..2d7241a 100644 --- a/BeReadyBackend/Endpoints/Achievements/GetUserAchievementsEndpoint.cs +++ b/BeReadyBackend/Endpoints/Achievements/GetUserAchievementsEndpoint.cs @@ -1,15 +1,12 @@ using BeReadyBackend.DTO.Achievements; -using BeReadyBackend.Models; using BeReadyBackend.Repositories; using BeReadyBackend.Services; using BeReadyBackend.Specifications.UserAchievements; -using BeReadyBackend.Specifications.Users; using FastEndpoints; namespace BeReadyBackend.Endpoints.Achievements; public class GetUserAchievementsEndpoint( - UsersRepository usersRepository, UserAchievementsRepository userAchievementsRepository, UserService userService) : EndpointWithoutRequest> @@ -22,14 +19,6 @@ public class GetUserAchievementsEndpoint( public override async Task HandleAsync(CancellationToken ct) { int userId = userService.GetUserIdFromToken(); - - User? user = await usersRepository.SingleOrDefaultAsync(new GetUserByIdSpec(userId), ct); - - if (user is null) - { - await Send.NotFoundAsync(ct); - return; - } List userAchievements = await userAchievementsRepository.ProjectToListAsync(new GetUserAchievementByUserIdSpec(userId), ct); diff --git a/BeReadyBackend/Endpoints/Achievements/UnlockAchievementEndpoint.cs b/BeReadyBackend/Endpoints/Achievements/UnlockAchievementEndpoint.cs index 654ee31..45fc0b3 100644 --- a/BeReadyBackend/Endpoints/Achievements/UnlockAchievementEndpoint.cs +++ b/BeReadyBackend/Endpoints/Achievements/UnlockAchievementEndpoint.cs @@ -4,7 +4,6 @@ using BeReadyBackend.Repositories; using BeReadyBackend.Services; using BeReadyBackend.Specifications.Achievements; using BeReadyBackend.Specifications.UserAchievements; -using BeReadyBackend.Specifications.Users; using FastEndpoints; namespace BeReadyBackend.Endpoints.Achievements; @@ -12,7 +11,6 @@ namespace BeReadyBackend.Endpoints.Achievements; public class UnlockAchievementEndpoint( UserAchievementsRepository userAchievementsRepository, AchievementsRepository achievementsRepository, - UsersRepository usersRepository, UserService userService, AutoMapper.IMapper mapper) : Endpoint { @@ -25,10 +23,9 @@ public class UnlockAchievementEndpoint( { int userId = userService.GetUserIdFromToken(); - User? user = await usersRepository.SingleOrDefaultAsync(new GetUserByIdSpec(userId), ct); Achievement? achievement = await achievementsRepository.SingleOrDefaultAsync(new GetAchievementByIdSpec(req.AchievementId), ct); - if (user is null || achievement is null) + if (achievement is null) { await Send.NotFoundAsync(ct); return; diff --git a/BeReadyBackend/Endpoints/Friends/AcceptFriendRequestEndpoint.cs b/BeReadyBackend/Endpoints/Friends/AcceptFriendRequestEndpoint.cs new file mode 100644 index 0000000..cf216dc --- /dev/null +++ b/BeReadyBackend/Endpoints/Friends/AcceptFriendRequestEndpoint.cs @@ -0,0 +1,45 @@ +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Services; +using BeReadyBackend.Specifications.Friends; +using FastEndpoints; + +namespace BeReadyBackend.Endpoints.Friends; + +public class AcceptFriendRequest +{ + public int FriendId { get; set; } +} + +public class AcceptFriendRequestEndpoint(UserService userService, UserFriendsRepository userFriendsRepository) : Endpoint +{ + public override void Configure() + { + Put("/Friends/{@FriendId}/Request/"); + } + + public override async Task HandleAsync(AcceptFriendRequest req, CancellationToken ct) + { + int userId = userService.GetUserIdFromToken(); + UserFriend? userFriend = await userFriendsRepository.SingleOrDefaultAsync(new GetFriendRequestByIdsSpec(req.FriendId, userId), ct); + + if (userFriend is null) + { + await Send.NotFoundAsync(ct); + return; + } + + userFriend.IsAccepted = true; + + UserFriend friend = new() + { + UserId = userId, + FriendId = req.FriendId, + IsAccepted = true + }; + + await userFriendsRepository.AddAsync(friend, ct); + await userFriendsRepository.SaveChangesAsync(ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Friends/DeleteFriendEndpoint.cs b/BeReadyBackend/Endpoints/Friends/DeleteFriendEndpoint.cs new file mode 100644 index 0000000..af27f2b --- /dev/null +++ b/BeReadyBackend/Endpoints/Friends/DeleteFriendEndpoint.cs @@ -0,0 +1,37 @@ +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Services; +using BeReadyBackend.Specifications.Friends; +using FastEndpoints; + +namespace BeReadyBackend.Endpoints.Friends; + +public class DeleteFriendRequest +{ + public int FriendId { get; set; } +} + +public class DeleteFriendEndpoint(UserFriendsRepository userFriendsRepository, UserService userService) : Endpoint +{ + public override void Configure() + { + Delete("/Friends/"); + } + + public override async Task HandleAsync(DeleteFriendRequest req, CancellationToken ct) + { + int userId = userService.GetUserIdFromToken(); + UserFriend? userFriend = await userFriendsRepository.SingleOrDefaultAsync(new GetFriendByCriteriaSpec(userId, req.FriendId), ct); + UserFriend? friendUser = await userFriendsRepository.SingleOrDefaultAsync(new GetFriendByCriteriaSpec(req.FriendId, userId), ct); + + if (userFriend is null || friendUser is null) + { + await Send.NotFoundAsync(ct); + return; + } + + await userFriendsRepository.DeleteAsync(userFriend, ct); + await userFriendsRepository.DeleteAsync(friendUser, ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Friends/GetAllFriendRequestsEndpoint.cs b/BeReadyBackend/Endpoints/Friends/GetAllFriendRequestsEndpoint.cs new file mode 100644 index 0000000..8be6723 --- /dev/null +++ b/BeReadyBackend/Endpoints/Friends/GetAllFriendRequestsEndpoint.cs @@ -0,0 +1,22 @@ +using BeReadyBackend.DTO.Friends; +using BeReadyBackend.Repositories; +using BeReadyBackend.Services; +using BeReadyBackend.Specifications.Friends; +using FastEndpoints; + +namespace BeReadyBackend.Endpoints.Friends; + +public class GetAllFriendRequestsEndpoint(UserFriendsRepository userFriendsRepository, UserService userService, AutoMapper.IMapper mapper) : EndpointWithoutRequest> +{ + public override void Configure() + { + Get("/Friends/Requests/"); + } + + public override async Task HandleAsync(CancellationToken ct) + { + int userId = userService.GetUserIdFromToken(); + + await Send.OkAsync(await userFriendsRepository.ProjectToListAsync(new GetFriendRequestSpec(userId), ct), ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Friends/GetAllFriendsEndpoint.cs b/BeReadyBackend/Endpoints/Friends/GetAllFriendsEndpoint.cs new file mode 100644 index 0000000..09a1fe0 --- /dev/null +++ b/BeReadyBackend/Endpoints/Friends/GetAllFriendsEndpoint.cs @@ -0,0 +1,23 @@ +using BeReadyBackend.DTO.Friends; +using BeReadyBackend.Repositories; +using BeReadyBackend.Services; +using BeReadyBackend.Specifications.Friends; +using FastEndpoints; + +namespace BeReadyBackend.Endpoints.Friends; + +public class GetAllFriendsEndpoint(UserFriendsRepository userFriendsRepository, UsersRepository usersRepository, UserService userService, AutoMapper.IMapper mapper) + : EndpointWithoutRequest> +{ + public override void Configure() + { + Get("/Friends/"); + } + + public override async Task HandleAsync(CancellationToken ct) + { + int userId = userService.GetUserIdFromToken(); + + await Send.OkAsync(await userFriendsRepository.ProjectToListAsync(new GetFriendsByUserIdSpec(userId), ct), ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Friends/RejectFriendRequestEndpoint.cs b/BeReadyBackend/Endpoints/Friends/RejectFriendRequestEndpoint.cs new file mode 100644 index 0000000..9495050 --- /dev/null +++ b/BeReadyBackend/Endpoints/Friends/RejectFriendRequestEndpoint.cs @@ -0,0 +1,35 @@ +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Services; +using BeReadyBackend.Specifications.Friends; +using FastEndpoints; + +namespace BeReadyBackend.Endpoints.Friends; + +public class RejectFriendRequest +{ + public int FriendId { get; set; } +} + +public class RejectFriendRequestEndpoint(UserService userService, UserFriendsRepository userFriendsRepository) : Endpoint +{ + public override void Configure() + { + Delete("/Friends/{@FriendId}/Request/"); + } + + public override async Task HandleAsync(RejectFriendRequest req, CancellationToken ct) + { + int userId = userService.GetUserIdFromToken(); + UserFriend? userFriend = await userFriendsRepository.SingleOrDefaultAsync(new GetFriendRequestByIdsSpec(req.FriendId, userId), ct); + + if (userFriend is null) + { + await Send.NotFoundAsync(ct); + return; + } + + await userFriendsRepository.DeleteAsync(userFriend, ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Friends/SendFriendRequestEndpoint.cs b/BeReadyBackend/Endpoints/Friends/SendFriendRequestEndpoint.cs new file mode 100644 index 0000000..5d4658c --- /dev/null +++ b/BeReadyBackend/Endpoints/Friends/SendFriendRequestEndpoint.cs @@ -0,0 +1,39 @@ +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Services; +using BeReadyBackend.Specifications.Friends; +using FastEndpoints; + +namespace BeReadyBackend.Endpoints.Friends; + +public class SendFriendRequest +{ + public int FriendId { get; set; } +} + +public class SendFriendRequestEndpoint(UserFriendsRepository userFriendsRepository, UserService userService, AutoMapper.IMapper mapper) : Endpoint +{ + public override void Configure() + { + Post("/Friends/{@Id}/", x => new {x.FriendId}); + } + + public override async Task HandleAsync(SendFriendRequest req, CancellationToken ct) + { + int userId = userService.GetUserIdFromToken(); + UserFriend? userFriend = await userFriendsRepository.SingleOrDefaultAsync(new GetFriendByCriteriaSpec(userId, req.FriendId), ct); + + if (userFriend is not null) + { + await Send.StringAsync("Cet utilisateur est déjà ami avec cette personne", 400, cancellation: ct); + return; + } + + userFriend = mapper.Map(req); + userFriend.UserId = userId; + userFriend.IsAccepted = false; + + await userFriendsRepository.AddAsync(userFriend, ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Users/DeleteUserEndpoint.cs b/BeReadyBackend/Endpoints/Users/DeleteUserEndpoint.cs index da8cd28..0f90e65 100644 --- a/BeReadyBackend/Endpoints/Users/DeleteUserEndpoint.cs +++ b/BeReadyBackend/Endpoints/Users/DeleteUserEndpoint.cs @@ -1,5 +1,4 @@ -using BeReadyBackend.Models; -using BeReadyBackend.Repositories; +using BeReadyBackend.Repositories; using BeReadyBackend.Services; using BeReadyBackend.Specifications.Users; using FastEndpoints; @@ -17,15 +16,7 @@ public class DeleteUserEndpoint(UsersRepository usersRepository, UserService use { int userId = userService.GetUserIdFromToken(); - User? user = await usersRepository.SingleOrDefaultAsync(new GetUserByIdSpec(userId), ct); - - if (user == null) - { - await Send.NotFoundAsync(ct); - return; - } - - await usersRepository.DeleteAsync(user, ct); + await usersRepository.DeleteAsync((await usersRepository.SingleOrDefaultAsync(new GetUserByIdSpec(userId), ct))!, ct); await Send.OkAsync(ct); } } \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Users/GetUserDetailsEndpoint.cs b/BeReadyBackend/Endpoints/Users/GetUserDetailsEndpoint.cs index 6663b7a..7d9a520 100644 --- a/BeReadyBackend/Endpoints/Users/GetUserDetailsEndpoint.cs +++ b/BeReadyBackend/Endpoints/Users/GetUserDetailsEndpoint.cs @@ -19,12 +19,6 @@ public class GetUserDetailsEndpoint(UsersRepository usersRepository, UserService int userId = userService.GetUserIdFromToken(); User? user = await usersRepository.SingleOrDefaultAsync(new GetUserByIdSpec(userId), ct); - - if (user is null) - { - await Send.NotFoundAsync(ct); - return; - } await Send.OkAsync(mapper.Map(user), ct); } diff --git a/BeReadyBackend/MappingProfiles/EntityToDtoMappings.cs b/BeReadyBackend/MappingProfiles/EntityToDtoMappings.cs index 50761c7..26feab1 100644 --- a/BeReadyBackend/MappingProfiles/EntityToDtoMappings.cs +++ b/BeReadyBackend/MappingProfiles/EntityToDtoMappings.cs @@ -1,5 +1,6 @@ using AutoMapper; using BeReadyBackend.DTO.Achievements; +using BeReadyBackend.DTO.Friends; using BeReadyBackend.DTO.Users; using BeReadyBackend.Models; @@ -35,5 +36,13 @@ public class EntityToDtoMappings : Profile .ForMember(dest => dest.ChallengeTitle, opt => opt.MapFrom(src => src.Title)) .ForMember(dest => dest.ChallengeDescription, opt => opt.MapFrom(src => src.Description)) .ForMember(dest => dest.ChallengeDuration, opt => opt.MapFrom(src => src.Duration)); + + CreateMap() + .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.Friend!.Username)) + .ForMember(dest => dest.Score, opt => opt.MapFrom(src => src.Friend!.Score)); + + CreateMap() + .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.User!.Username)) + .ForMember(dest => dest.Score, opt => opt.MapFrom(src => src.User!.Score)); } } \ No newline at end of file diff --git a/BeReadyBackend/Specifications/Friends/GetFriendByCriteriaSpec.cs b/BeReadyBackend/Specifications/Friends/GetFriendByCriteriaSpec.cs new file mode 100644 index 0000000..aef9ad9 --- /dev/null +++ b/BeReadyBackend/Specifications/Friends/GetFriendByCriteriaSpec.cs @@ -0,0 +1,13 @@ +using Ardalis.Specification; +using BeReadyBackend.Models; + +namespace BeReadyBackend.Specifications.Friends; + +public class GetFriendByCriteriaSpec : SingleResultSpecification +{ + public GetFriendByCriteriaSpec(int userId, int friendId) + { + Query + .Where(x => x.UserId == userId && x.FriendId == friendId && x.IsAccepted); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Specifications/Friends/GetFriendRequestByIdsSpec.cs b/BeReadyBackend/Specifications/Friends/GetFriendRequestByIdsSpec.cs new file mode 100644 index 0000000..f7453f8 --- /dev/null +++ b/BeReadyBackend/Specifications/Friends/GetFriendRequestByIdsSpec.cs @@ -0,0 +1,13 @@ +using Ardalis.Specification; +using BeReadyBackend.Models; + +namespace BeReadyBackend.Specifications.Friends; + +public class GetFriendRequestByIdsSpec : SingleResultSpecification +{ + public GetFriendRequestByIdsSpec(int userId, int friendId) + { + Query + .Where(x => x.UserId == userId && x.FriendId == friendId && !x.IsAccepted); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Specifications/Friends/GetFriendRequestSpec.cs b/BeReadyBackend/Specifications/Friends/GetFriendRequestSpec.cs new file mode 100644 index 0000000..cd34059 --- /dev/null +++ b/BeReadyBackend/Specifications/Friends/GetFriendRequestSpec.cs @@ -0,0 +1,14 @@ +using Ardalis.Specification; +using BeReadyBackend.Models; + +namespace BeReadyBackend.Specifications.Friends; + +public class GetFriendRequestSpec : Specification +{ + public GetFriendRequestSpec(int friendId) + { + Query + .Include(x => x.User) + .Where(x => !x.IsAccepted && x.FriendId == friendId); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Specifications/Friends/GetFriendsByUserIdSpec.cs b/BeReadyBackend/Specifications/Friends/GetFriendsByUserIdSpec.cs new file mode 100644 index 0000000..62d34fc --- /dev/null +++ b/BeReadyBackend/Specifications/Friends/GetFriendsByUserIdSpec.cs @@ -0,0 +1,15 @@ +using Ardalis.Specification; +using BeReadyBackend.Models; + +namespace BeReadyBackend.Specifications.Friends; + +public class GetFriendsByUserIdSpec : Specification +{ + public GetFriendsByUserIdSpec(int userId) + { + Query + .Include(x => x.User) + .Include(x => x.Friend) + .Where(x => x.UserId == userId && x.IsAccepted); + } +} \ No newline at end of file