From f1b4f53cd42a3735b05301473b26d571c8ddc253 Mon Sep 17 00:00:00 2001 From: vidufour1 Date: Sat, 16 Mar 2024 21:23:55 +0100 Subject: [PATCH] share tactic to team and user --- API/API.csproj.user | 6 ++++ API/Controllers/TacticsController.cs | 1 + API/Controllers/TeamsController.cs | 33 +++++++++++++++++- API/Controllers/UsersController.cs | 42 +++++++++++++++++++++++ AppContext/AppContext.cs | 2 ++ AppContext/Entities/SharedTacticEntity.cs | 12 +++++++ Converters/ModelToEntities.cs | 5 +++ DbServices/DbTacticService.cs | 27 +++++++++++++++ DbServices/DbTeamService.cs | 29 ++++++++++++++++ DbServices/DbUserService.cs | 23 +++++++++++++ Model/SharedTactic.cs | 3 ++ Services/ITacticService.cs | 3 ++ Services/ITeamService.cs | 4 +++ Services/UserService.cs | 3 ++ UnitTests/UserControllerTest.cs | 10 ++++++ 15 files changed, 202 insertions(+), 1 deletion(-) create mode 100644 API/API.csproj.user create mode 100644 AppContext/Entities/SharedTacticEntity.cs create mode 100644 Model/SharedTactic.cs diff --git a/API/API.csproj.user b/API/API.csproj.user new file mode 100644 index 0000000..9ff5820 --- /dev/null +++ b/API/API.csproj.user @@ -0,0 +1,6 @@ + + + + https + + \ No newline at end of file diff --git a/API/Controllers/TacticsController.cs b/API/Controllers/TacticsController.cs index 9452f11..518df42 100644 --- a/API/Controllers/TacticsController.cs +++ b/API/Controllers/TacticsController.cs @@ -151,4 +151,5 @@ public class TacticController(ITacticService service, IContextAccessor accessor) await service.SetTacticStepContent(tacticId, stepId, JsonSerializer.Serialize(req.Content)); return Ok(); } + } \ No newline at end of file diff --git a/API/Controllers/TeamsController.cs b/API/Controllers/TeamsController.cs index aecfc67..c674085 100644 --- a/API/Controllers/TeamsController.cs +++ b/API/Controllers/TeamsController.cs @@ -1,6 +1,7 @@ using System.ComponentModel.DataAnnotations; using API.Context; using API.Validation; +using AppContext.Entities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Model; @@ -10,7 +11,7 @@ namespace API.Controllers; [ApiController] [Authorize] -public class TeamsController(ITeamService service, IContextAccessor accessor) : ControllerBase +public class TeamsController(ITeamService service, ITacticService tactics,IContextAccessor accessor) : ControllerBase { public record CreateTeamRequest( [Name] string Name, @@ -74,4 +75,34 @@ public class TeamsController(ITeamService service, IContextAccessor accessor) : var removed = await service.RemoveMember(teamId, userId); return removed ? Ok() : NotFound(); } + + public record ShareTacticToTeamRequest( + int TacticId, + int TeamId + ); + + [HttpPost("/team/share-tactic")] + [Authorize] + public async Task ShareTactic([FromBody] ShareTacticToTeamRequest sharedTactic) + { + var userId = accessor.CurrentUserId(HttpContext); + var success = await tactics.ShareTactic(sharedTactic.TacticId, null, sharedTactic.TeamId); + + return success ? Ok() : BadRequest(); + } + + [HttpGet("/tactics/shared/team/{teamId:int}")] + [Authorize] + public async Task GetSharedTacticsToTeam(int teamId) + { + var currentUserId = accessor.CurrentUserId(HttpContext); + + if (!await service.IsUserInTeam(currentUserId, teamId)) + { + return Unauthorized(); + } + + var sharedTactics = await service.GetSharedTacticsToTeam(teamId); + return sharedTactics != null ? Ok(sharedTactics) : NotFound(); + } } \ No newline at end of file diff --git a/API/Controllers/UsersController.cs b/API/Controllers/UsersController.cs index 2bb6a4f..e5e7f37 100644 --- a/API/Controllers/UsersController.cs +++ b/API/Controllers/UsersController.cs @@ -1,6 +1,7 @@ using System.Runtime.CompilerServices; using API.Context; using API.DTO; +using AppContext.Entities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Model; @@ -35,4 +36,45 @@ public class UsersController(IUserService users, ITeamService teams, ITacticServ var userTactics = await tactics.ListTacticsOf(userId); return new GetUserDataResponse(userTeams.ToArray(), userTactics.Select(t => t.ToDto()).ToArray()); } + + public record ShareTacticToUserRequest( + int TacticId, + int UserId + ); + + [HttpPost("/user/share-tactic")] + [Authorize] + public async Task ShareTactic([FromBody] ShareTacticToUserRequest sharedTactic) + { + var currentUserId = accessor.CurrentUserId(HttpContext); + var tactic = await tactics.GetTactic(sharedTactic.TacticId); + + if (tactic == null) + { + return NotFound(); + } + + if (currentUserId != tactic.OwnerId) + { + return Unauthorized(); + } + + var result = await tactics.ShareTactic(sharedTactic.TacticId, sharedTactic.UserId, null); + return result ? Ok() : NotFound(); + } + + + [HttpGet("/tactics/shared/user/{userId:int}")] + [Authorize] + public async Task GetSharedTacticsToUser(int userId) + { + var currentUserId = accessor.CurrentUserId(HttpContext); + if (currentUserId != userId) + { + return Unauthorized(); + } + + var sharedTactics = await users.GetSharedTacticsToUser(userId); + return sharedTactics != null ? Ok(sharedTactics) : NotFound(); + } } \ No newline at end of file diff --git a/AppContext/AppContext.cs b/AppContext/AppContext.cs index f875684..e47c05d 100644 --- a/AppContext/AppContext.cs +++ b/AppContext/AppContext.cs @@ -13,7 +13,9 @@ public class AppContext : DbContext public DbSet Teams { get; init; } public DbSet Members { get; init; } public DbSet TacticSteps { get; set; } + public DbSet SharedTactics { get; set; } + public AppContext() { diff --git a/AppContext/Entities/SharedTacticEntity.cs b/AppContext/Entities/SharedTacticEntity.cs new file mode 100644 index 0000000..852dd92 --- /dev/null +++ b/AppContext/Entities/SharedTacticEntity.cs @@ -0,0 +1,12 @@ +using System.ComponentModel.DataAnnotations; + +namespace AppContext.Entities +{ + public class SharedTacticEntity + { + [Key] public int Id { get; set; } + public int TacticId { get; set; } + public int? SharedWithUserId { get; set; } + public int? SharedWithTeamId { get; set; } + } +} \ No newline at end of file diff --git a/Converters/ModelToEntities.cs b/Converters/ModelToEntities.cs index 6c8006c..001aed6 100644 --- a/Converters/ModelToEntities.cs +++ b/Converters/ModelToEntities.cs @@ -37,4 +37,9 @@ public static class EntitiesToModels { return new Member(entity.TeamId, entity.UserId, entity.Role); } + + public static SharedTactic ToModel(this SharedTacticEntity entity) + { + return new SharedTactic(entity.Id, entity.TacticId, entity.SharedWithUserId, entity.SharedWithTeamId); + } } \ No newline at end of file diff --git a/DbServices/DbTacticService.cs b/DbServices/DbTacticService.cs index 1fcb1b3..3312cd1 100644 --- a/DbServices/DbTacticService.cs +++ b/DbServices/DbTacticService.cs @@ -152,4 +152,31 @@ public class DbTacticService(AppContext.AppContext context) : ITacticService return await context.SaveChangesAsync() > 0; } + + public async Task ShareTactic(int tacticId, int? userId, int? teamId) + { + var sharedTactic = new SharedTacticEntity + { + TacticId = tacticId, + SharedWithUserId = userId, + SharedWithTeamId = teamId + }; + + await context.SharedTactics.AddAsync(sharedTactic); + return await context.SaveChangesAsync() > 0; + } + + public async Task UnshareTactic(int tacticId, int? userId, int? teamId) + { + var sharedTactic = await context.SharedTactics + .FirstOrDefaultAsync(st => st.TacticId == tacticId && st.SharedWithUserId == userId && st.SharedWithTeamId == teamId); + + if (sharedTactic == null) + { + return false; + } + + context.SharedTactics.Remove(sharedTactic); + return await context.SaveChangesAsync() > 0; + } } \ No newline at end of file diff --git a/DbServices/DbTeamService.cs b/DbServices/DbTeamService.cs index 6ca6e1e..82aa4a0 100644 --- a/DbServices/DbTeamService.cs +++ b/DbServices/DbTeamService.cs @@ -83,6 +83,29 @@ public class DbTeamService(AppContext.AppContext context) : ITeamService return await context.SaveChangesAsync() > 0; } + public async Task> GetSharedTacticsToTeam(int teamId) + { + var sharedTactics = await context.SharedTactics + .Where(st => st.SharedWithTeamId == teamId) + .ToListAsync(); + + var tactics = new List(); + foreach (var sharedTactic in sharedTactics) + { + var tactic = await context.Tactics + .Where(t => t.Id == sharedTactic.TacticId) + .Select(t => t.ToModel()) + .FirstOrDefaultAsync(); + + if (tactic != null) + { + tactics.Add(tactic); + } + } + + return tactics; + } + public IEnumerable GetMembersOf(int teamId) { @@ -121,4 +144,10 @@ public class DbTeamService(AppContext.AppContext context) : ITeamService .ExecuteDeleteAsync(); return await context.SaveChangesAsync() > 0; } + + public async Task IsUserInTeam(int userId, int teamId) + { + return await context.Members + .AnyAsync(m => m.TeamId == teamId && m.UserId == userId); + } } \ No newline at end of file diff --git a/DbServices/DbUserService.cs b/DbServices/DbUserService.cs index 83aa821..3d1268a 100644 --- a/DbServices/DbUserService.cs +++ b/DbServices/DbUserService.cs @@ -94,4 +94,27 @@ public class DbUserService(AppContext.AppContext context) : IUserService .FirstOrDefaultAsync(u => u.Email == email)) ?.ToModel(); } + + public async Task> GetSharedTacticsToUser(int userId) + { + var sharedTactics = await context.SharedTactics + .Where(st => st.SharedWithUserId == userId) + .ToListAsync(); + + var tactics = new List(); + foreach (var sharedTactic in sharedTactics) + { + var tactic = await context.Tactics + .Where(t => t.Id == sharedTactic.TacticId) + .Select(t => t.ToModel()) + .FirstOrDefaultAsync(); + + if (tactic != null) + { + tactics.Add(tactic); + } + } + + return tactics; + } } \ No newline at end of file diff --git a/Model/SharedTactic.cs b/Model/SharedTactic.cs new file mode 100644 index 0000000..8fc29e8 --- /dev/null +++ b/Model/SharedTactic.cs @@ -0,0 +1,3 @@ +namespace Model; + +public record SharedTactic(int Id, int TacticId, int? SharedWithUserId, int? SharedWithTeamId); \ No newline at end of file diff --git a/Services/ITacticService.cs b/Services/ITacticService.cs index 98d68e7..e8bb83d 100644 --- a/Services/ITacticService.cs +++ b/Services/ITacticService.cs @@ -23,4 +23,7 @@ public interface ITacticService public Task> ListUserTactics(int userId); public Task AddTacticStep(int tacticId, int parentStepId, string initialJson); public Task RemoveTacticStep(int tacticId, int stepId); + + public Task ShareTactic(int tacticId, int? userId, int? teamId); + public Task UnshareTactic(int tacticId, int? userId, int? teamId); } \ No newline at end of file diff --git a/Services/ITeamService.cs b/Services/ITeamService.cs index 63deaeb..90efd1c 100644 --- a/Services/ITeamService.cs +++ b/Services/ITeamService.cs @@ -23,4 +23,8 @@ public interface ITeamService public Task UpdateTeam(Team team); + public Task> GetSharedTacticsToTeam(int teamId); + + public Task IsUserInTeam(int userId, int teamId); + } \ No newline at end of file diff --git a/Services/UserService.cs b/Services/UserService.cs index 4e67597..558c42d 100644 --- a/Services/UserService.cs +++ b/Services/UserService.cs @@ -21,5 +21,8 @@ public interface IUserService Task UpdateUser(User user); public Task Authorize(string email, string password); + + public Task> GetSharedTacticsToUser(int userId); + } \ No newline at end of file diff --git a/UnitTests/UserControllerTest.cs b/UnitTests/UserControllerTest.cs index cb98c44..a57b0f6 100644 --- a/UnitTests/UserControllerTest.cs +++ b/UnitTests/UserControllerTest.cs @@ -1,6 +1,8 @@ using API.Controllers; +using AppContext.Entities; using DbServices; using FluentAssertions; +using Microsoft.AspNetCore.Mvc; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; using Model; @@ -46,4 +48,12 @@ public class UsersControllerTest var result = await controller.GetUserData(); result.Should().BeEquivalentTo(new UsersController.GetUserDataResponse([], [])); } + + [Fact] + public async Task ShareTacticTest() + { + var controller = GetUserController(1); + var result = await controller.ShareTactic(new UsersController.ShareTacticToUserRequest(1, 2)); + result.Should().BeOfType(); + } } \ No newline at end of file