From 9301800d0bdb76b7aad79eaafd7211441823a874 Mon Sep 17 00:00:00 2001 From: sanchezvem Date: Sun, 22 Feb 2026 18:39:41 +0100 Subject: [PATCH] Created all endpoints to manage groups --- .../DTO/Groups/GetGroupRankingDto.cs | 8 +++ BeReadyBackend/DTO/Groups/GetProofDto.cs | 9 +++ .../Groups/DeleteUserFromGroupEndpoint.cs | 44 +++++++++++++++ .../Endpoints/Groups/GetAllProofsEndpoint.cs | 25 +++++++++ .../Groups/GetGroupRankingEndpoint.cs | 54 ++++++++++++++++++ .../Groups/PatchGroupStatusEndpoint.cs | 34 ++++++++++++ .../Groups/PatchGroupUserProofEndpoint.cs | 49 +++++++++++++++++ .../Groups/PatchGroupUserRoleEndpoint.cs | 43 +++++++++++++++ .../Endpoints/Groups/PatchVoteEndpoint.cs | 49 +++++++++++++++++ .../Endpoints/Groups/StartVoteEndpoint.cs | 55 +++++++++++++++++++ .../MappingProfiles/DtoToEntityMappings.cs | 2 +- .../MappingProfiles/EntityToDtoMappings.cs | 6 ++ .../Specifications/Groups/GetGroupRankSpec.cs | 14 +++++ .../Groups/GetUserGroupDetailsByIdSpec.cs | 13 +++++ .../Groups/GetUserInGroupByIdsSpec.cs | 14 +++++ 15 files changed, 418 insertions(+), 1 deletion(-) create mode 100644 BeReadyBackend/DTO/Groups/GetGroupRankingDto.cs create mode 100644 BeReadyBackend/DTO/Groups/GetProofDto.cs create mode 100644 BeReadyBackend/Endpoints/Groups/DeleteUserFromGroupEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Groups/GetAllProofsEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Groups/GetGroupRankingEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Groups/PatchGroupStatusEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Groups/PatchGroupUserProofEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Groups/PatchGroupUserRoleEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Groups/PatchVoteEndpoint.cs create mode 100644 BeReadyBackend/Endpoints/Groups/StartVoteEndpoint.cs create mode 100644 BeReadyBackend/Specifications/Groups/GetGroupRankSpec.cs create mode 100644 BeReadyBackend/Specifications/Groups/GetUserGroupDetailsByIdSpec.cs create mode 100644 BeReadyBackend/Specifications/Groups/GetUserInGroupByIdsSpec.cs diff --git a/BeReadyBackend/DTO/Groups/GetGroupRankingDto.cs b/BeReadyBackend/DTO/Groups/GetGroupRankingDto.cs new file mode 100644 index 0000000..289bf2b --- /dev/null +++ b/BeReadyBackend/DTO/Groups/GetGroupRankingDto.cs @@ -0,0 +1,8 @@ +namespace BeReadyBackend.DTO.Groups; + +public class GetGroupRankingDto +{ + public int UserId { get; set; } + public string? Username { get; set; } + public int Score { get; set; } +} \ No newline at end of file diff --git a/BeReadyBackend/DTO/Groups/GetProofDto.cs b/BeReadyBackend/DTO/Groups/GetProofDto.cs new file mode 100644 index 0000000..31c0ad3 --- /dev/null +++ b/BeReadyBackend/DTO/Groups/GetProofDto.cs @@ -0,0 +1,9 @@ +namespace BeReadyBackend.DTO.Groups; + +public class GetProofDto +{ + public int UserId { get; set; } + public string? Username { get; set; } + public string? Proof { get; set; } + public int Score { get; set; } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Groups/DeleteUserFromGroupEndpoint.cs b/BeReadyBackend/Endpoints/Groups/DeleteUserFromGroupEndpoint.cs new file mode 100644 index 0000000..171f486 --- /dev/null +++ b/BeReadyBackend/Endpoints/Groups/DeleteUserFromGroupEndpoint.cs @@ -0,0 +1,44 @@ +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Services; +using BeReadyBackend.Specifications.Groups; +using FastEndpoints; +using Group = BeReadyBackend.Models.Group; + +namespace BeReadyBackend.Endpoints.Groups; + +public class DeleteUserFromGroupRequest +{ + public int GroupId { get; set; } + public int UserId { get; set; } +} + +public class DeleteUserFromGroupEndpoint(UserGroupsRepository userGroupsRepository, UserService userService) : Endpoint +{ + public override void Configure() + { + Delete("/Groups/{@GroupId}/Users/{@UserId}", x => new { x.GroupId, x.UserId }); + } + + public override async Task HandleAsync(DeleteUserFromGroupRequest req, CancellationToken ct) + { + int userId = userService.GetUserIdFromToken(); + UserGroup? member = await userGroupsRepository.SingleOrDefaultAsync(new GetUserInGroupByIdsSpec(req.GroupId, req.UserId), ct); + + if (member is null) + { + await Send.NotFoundAsync(ct); + return; + } + + if (member.Grade != "Admin" && userId != req.UserId) + { + await Send.StringAsync("Vous n'avez pas les droits pour supprimer ce membre du groupe", 400, cancellation: ct); + return; + } + + await userGroupsRepository.DeleteAsync(member, ct); + await userGroupsRepository.SaveChangesAsync(ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Groups/GetAllProofsEndpoint.cs b/BeReadyBackend/Endpoints/Groups/GetAllProofsEndpoint.cs new file mode 100644 index 0000000..de56f2b --- /dev/null +++ b/BeReadyBackend/Endpoints/Groups/GetAllProofsEndpoint.cs @@ -0,0 +1,25 @@ +using BeReadyBackend.DTO.Groups; +using BeReadyBackend.Repositories; +using BeReadyBackend.Specifications.Groups; +using FastEndpoints; + +namespace BeReadyBackend.Endpoints.Groups; + +public class GroupProofRequest +{ + public int Id { get; set; } +} + +public class GetAllProofsEndpoint(UserGroupsRepository userGroupsRepository) : Endpoint> +{ + public override void Configure() + { + Get("/Groups/{@Id}/Proofs/", x => new { x.Id }); + } + + public override async Task HandleAsync(GroupProofRequest req, CancellationToken ct) + { + List usersGroup = await userGroupsRepository.ProjectToListAsync(new GetUserGroupDetailsByIdSpec(req.Id), ct); + await Send.OkAsync(usersGroup, ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Groups/GetGroupRankingEndpoint.cs b/BeReadyBackend/Endpoints/Groups/GetGroupRankingEndpoint.cs new file mode 100644 index 0000000..dbc50bf --- /dev/null +++ b/BeReadyBackend/Endpoints/Groups/GetGroupRankingEndpoint.cs @@ -0,0 +1,54 @@ +using BeReadyBackend.DTO.Groups; +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Specifications.Groups; +using BeReadyBackend.Specifications.Users; +using FastEndpoints; +using Group = BeReadyBackend.Models.Group; + +namespace BeReadyBackend.Endpoints.Groups; + +public class GroupRankingRequest +{ + public int Id { get; set; } +} + +public class GetGroupRankingEndpoint( + GroupsRepository groupsRepository, + UsersRepository usersRepository, + UserGroupsRepository userGroupsRepository) + : Endpoint> +{ + public override void Configure() + { + Get("/Groups/{@Id}/GroupRank", x => new { x.Id }); + } + + public override async Task HandleAsync(GroupRankingRequest req, CancellationToken ct) + { + Group? group = await groupsRepository.SingleOrDefaultAsync(new GetGroupByIdSpec(req.Id), ct); + if (group is null) + { + await Send.NotFoundAsync(ct); + return; + } + + foreach (UserGroup member in group.UserGroups!) + { + if (member.Proof is null) member.Score -= 2; + } + await userGroupsRepository.SaveChangesAsync(ct); + + List groupScore = await userGroupsRepository.ProjectToListAsync(new GetGroupRankSpec(req.Id), ct); + + int[] points = [5, 3, 1]; + for (int i = 0; i < groupScore.Count && i < 3; i++) + { + User? user = await usersRepository.SingleOrDefaultAsync(new GetUserByIdSpec(groupScore[i].UserId), ct); + if (user != null) user.Score += points[i]; + } + await usersRepository.SaveChangesAsync(ct); + + await Send.OkAsync(groupScore, ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Groups/PatchGroupStatusEndpoint.cs b/BeReadyBackend/Endpoints/Groups/PatchGroupStatusEndpoint.cs new file mode 100644 index 0000000..6e33f1b --- /dev/null +++ b/BeReadyBackend/Endpoints/Groups/PatchGroupStatusEndpoint.cs @@ -0,0 +1,34 @@ +using BeReadyBackend.Repositories; +using BeReadyBackend.Specifications.Groups; +using FastEndpoints; +using Group = BeReadyBackend.Models.Group; + +namespace BeReadyBackend.Endpoints.Groups; + +public class StatusRequest +{ + public int GroupId { get; set; } +} + +public class PatchGroupStatusEndpoint(GroupsRepository groupsRepository) : Endpoint +{ + public override void Configure() + { + Patch("/Groups/{@GroupId}/Status/", x => new { x.GroupId }); + } + + public override async Task HandleAsync(StatusRequest req, CancellationToken ct) + { + Group? group = await groupsRepository.SingleOrDefaultAsync(new GetGroupByIdSpec(req.GroupId), ct); + + if (group is null) + { + await Send.NotFoundAsync(ct); + return; + } + + group.IsFinished = true; + await groupsRepository.SaveChangesAsync(ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Groups/PatchGroupUserProofEndpoint.cs b/BeReadyBackend/Endpoints/Groups/PatchGroupUserProofEndpoint.cs new file mode 100644 index 0000000..1d25495 --- /dev/null +++ b/BeReadyBackend/Endpoints/Groups/PatchGroupUserProofEndpoint.cs @@ -0,0 +1,49 @@ +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Services; +using BeReadyBackend.Specifications.Groups; +using FastEndpoints; + +namespace BeReadyBackend.Endpoints.Groups; + +public class UserProofRequest +{ + public int GroupId { get; set; } + public string? Proof { get; set; } +} + +public class PatchGroupUserProofEndpoint(UserGroupsRepository userGroupsRepository, UserService userService) : Endpoint +{ + public override void Configure() + { + Patch("/Groups/{@GroupId}/Users/Proof/", x => new { x.GroupId }); + } + + public override async Task HandleAsync(UserProofRequest req, CancellationToken ct) + { + int userId = userService.GetUserIdFromToken(); + UserGroup? member = await userGroupsRepository.SingleOrDefaultAsync(new GetUserInGroupByIdsSpec(req.GroupId, userId), ct); + + if (member is null) + { + await Send.NotFoundAsync(ct); + return; + } + + if (member.Proof is not null) + { + await Send.StringAsync("Vous avez déjà déposé une photo", 400, cancellation: ct); + return; + } + + if (member.Group!.IsFinished) + { + await Send.StringAsync("Ce défi est terminé", 400, cancellation: ct); + return; + } + + member.Proof = req.Proof; + await userGroupsRepository.SaveChangesAsync(ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Groups/PatchGroupUserRoleEndpoint.cs b/BeReadyBackend/Endpoints/Groups/PatchGroupUserRoleEndpoint.cs new file mode 100644 index 0000000..da6ed8c --- /dev/null +++ b/BeReadyBackend/Endpoints/Groups/PatchGroupUserRoleEndpoint.cs @@ -0,0 +1,43 @@ +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Services; +using BeReadyBackend.Specifications.Groups; +using FastEndpoints; + +namespace BeReadyBackend.Endpoints.Groups; + +public class UserRoleRequest +{ + public int GroupId { get; set; } + public int UserId { get; set; } +} + +public class PatchGroupUserRoleEndpoint(UserGroupsRepository userGroupsRepository, UserService userService) : Endpoint +{ + public override void Configure() + { + Patch("/Groups/{@GroupId}/Users/{@UserId}/Role/", x => new { x.GroupId, x.UserId }); + } + + public override async Task HandleAsync(UserRoleRequest req, CancellationToken ct) + { + int userId = userService.GetUserIdFromToken(); + UserGroup? member = await userGroupsRepository.SingleOrDefaultAsync(new GetUserInGroupByIdsSpec(req.GroupId, req.UserId), ct); + + if (member is null) + { + await Send.NotFoundAsync(ct); + return; + } + + if (member.Grade != "Admin" && userId != req.UserId) + { + await Send.StringAsync("Vous n'avez pas les droits pour changer le rôle de ce membre", 400, cancellation: ct); + return; + } + + member.Grade = "Admin"; + await userGroupsRepository.SaveChangesAsync(ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Groups/PatchVoteEndpoint.cs b/BeReadyBackend/Endpoints/Groups/PatchVoteEndpoint.cs new file mode 100644 index 0000000..74a9e7b --- /dev/null +++ b/BeReadyBackend/Endpoints/Groups/PatchVoteEndpoint.cs @@ -0,0 +1,49 @@ +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Services; +using BeReadyBackend.Specifications.Groups; +using FastEndpoints; + +namespace BeReadyBackend.Endpoints.Groups; + +public class UserVoteRequest +{ + public int GroupId { get; set; } + public int VotedProofId { get; set; } +} + +public class PatchVoteEndpoint(UserGroupsRepository userGroupsRepository, UserService userService) : Endpoint +{ + public override void Configure() + { + Patch("/Groups/{@GroupId}/Users/Vote/", x => new { x.GroupId }); + } + + public override async Task HandleAsync(UserVoteRequest req, CancellationToken ct) + { + int userId = userService.GetUserIdFromToken(); + UserGroup? member = await userGroupsRepository.SingleOrDefaultAsync(new GetUserInGroupByIdsSpec(req.GroupId, userId), ct); + + if (member is null) + { + await Send.NotFoundAsync(ct); + return; + } + + if (member.VotedProofId is not null) + { + await Send.StringAsync("Vous ne pouvez pas voter plusieurs fois", 400, cancellation: ct); + return; + } + + if (member.Group!.IsFinished) + { + await Send.StringAsync("Ce défi est terminé", 400, cancellation: ct); + return; + } + + member.VotedProofId = req.VotedProofId; + await userGroupsRepository.SaveChangesAsync(ct); + await Send.OkAsync(ct); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Endpoints/Groups/StartVoteEndpoint.cs b/BeReadyBackend/Endpoints/Groups/StartVoteEndpoint.cs new file mode 100644 index 0000000..f3ad82a --- /dev/null +++ b/BeReadyBackend/Endpoints/Groups/StartVoteEndpoint.cs @@ -0,0 +1,55 @@ +using BeReadyBackend.Models; +using BeReadyBackend.Repositories; +using BeReadyBackend.Services; +using BeReadyBackend.Specifications.Groups; +using FastEndpoints; +using Group = BeReadyBackend.Models.Group; + +namespace BeReadyBackend.Endpoints.Groups; + +public class GroupVoteRequest +{ + public int Id { get; set; } +} + +public class StartVoteEndpoint(UserGroupsRepository userGroupsRepository, GroupsRepository groupsRepository, UserService userService) : Endpoint +{ + public override void Configure() + { + Post("/Groups/{@Id}/Vote/", x => new { x.Id }); + } + + public override async Task HandleAsync(GroupVoteRequest req, CancellationToken ct) + { + Group? group = await groupsRepository.SingleOrDefaultAsync(new GetGroupByIdSpec(req.Id), ct); + + if (group is null) + { + await Send.NotFoundAsync(ct); + return; + } + + UserGroup? userGroup = group.UserGroups!.FirstOrDefault(x => x.UserId == userService.GetUserIdFromToken()); + + if (userGroup?.Grade != "Admin") + { + await Send.StringAsync("Vous ne pouvez pas lancer le vote", 400, cancellation: ct); + return; + } + + if (group.IsFinished) + { + await Send.StringAsync("Le défi est terminé", 400, cancellation: ct); + return; + } + + if (group.CreationDate.AddHours(group.Duration) > DateTime.Now) + { + await Send.StringAsync("Le défi n'est pas terminé", 400, cancellation: ct); + return; + } + + await Send.OkAsync(ct); + } + +} \ No newline at end of file diff --git a/BeReadyBackend/MappingProfiles/DtoToEntityMappings.cs b/BeReadyBackend/MappingProfiles/DtoToEntityMappings.cs index dcaca0f..a1760ca 100644 --- a/BeReadyBackend/MappingProfiles/DtoToEntityMappings.cs +++ b/BeReadyBackend/MappingProfiles/DtoToEntityMappings.cs @@ -17,6 +17,6 @@ public class DtoToEntityMappings : Profile CreateMap(); CreateMap() - .ForMember(dest => dest.UserGroups, opt => opt.Ignore());; + .ForMember(dest => dest.UserGroups, opt => opt.Ignore()); } } \ No newline at end of file diff --git a/BeReadyBackend/MappingProfiles/EntityToDtoMappings.cs b/BeReadyBackend/MappingProfiles/EntityToDtoMappings.cs index 0d03362..9076beb 100644 --- a/BeReadyBackend/MappingProfiles/EntityToDtoMappings.cs +++ b/BeReadyBackend/MappingProfiles/EntityToDtoMappings.cs @@ -64,5 +64,11 @@ public class EntityToDtoMappings : Profile CreateMap() .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.User!.Username)); + + CreateMap() + .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.User!.Username)); + + CreateMap() + .ForMember(dest => dest.Username, opt => opt.MapFrom(src => src.User!.Username)); } } \ No newline at end of file diff --git a/BeReadyBackend/Specifications/Groups/GetGroupRankSpec.cs b/BeReadyBackend/Specifications/Groups/GetGroupRankSpec.cs new file mode 100644 index 0000000..07e3b24 --- /dev/null +++ b/BeReadyBackend/Specifications/Groups/GetGroupRankSpec.cs @@ -0,0 +1,14 @@ +using Ardalis.Specification; +using BeReadyBackend.Models; + +namespace BeReadyBackend.Specifications.Groups; + +public class GetGroupRankSpec : Specification +{ + public GetGroupRankSpec(int id) + { + Query + .Where(x => x.GroupId == id) + .OrderByDescending(x => x.Score); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Specifications/Groups/GetUserGroupDetailsByIdSpec.cs b/BeReadyBackend/Specifications/Groups/GetUserGroupDetailsByIdSpec.cs new file mode 100644 index 0000000..96de6aa --- /dev/null +++ b/BeReadyBackend/Specifications/Groups/GetUserGroupDetailsByIdSpec.cs @@ -0,0 +1,13 @@ +using Ardalis.Specification; +using BeReadyBackend.Models; + +namespace BeReadyBackend.Specifications.Groups; + +public class GetUserGroupDetailsByIdSpec : Specification +{ + public GetUserGroupDetailsByIdSpec(int groupId) + { + Query + .Where(x => x.GroupId == groupId); + } +} \ No newline at end of file diff --git a/BeReadyBackend/Specifications/Groups/GetUserInGroupByIdsSpec.cs b/BeReadyBackend/Specifications/Groups/GetUserInGroupByIdsSpec.cs new file mode 100644 index 0000000..c973c82 --- /dev/null +++ b/BeReadyBackend/Specifications/Groups/GetUserInGroupByIdsSpec.cs @@ -0,0 +1,14 @@ +using Ardalis.Specification; +using BeReadyBackend.Models; + +namespace BeReadyBackend.Specifications.Groups; + +public class GetUserInGroupByIdsSpec : SingleResultSpecification +{ + public GetUserInGroupByIdsSpec(int groupId, int userId) + { + Query + .Include(x => x.Group) + .Where(x => x.GroupId == groupId && x.UserId == userId); + } +} \ No newline at end of file