Merge pull request 'shareTactic' (#2) from shareTactic into master
continuous-integration/drone/push Build is passing Details

Reviewed-on: #2
shared-tactic
Vivien DUFOUR 1 year ago
commit ea827253e4

@ -150,4 +150,5 @@ public class TacticController(ITacticService service, IContextAccessor accessor)
var found = await service.SetTacticStepContent(tacticId, stepId, JsonSerializer.Serialize(req.Content));
return found ? Ok() : NotFound();
}
}

@ -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,
@ -134,4 +135,51 @@ public class TeamsController(ITeamService service, IContextAccessor accessor) :
return Problem();
}
}
public record ShareTacticToTeamRequest(
int TacticId,
int TeamId
);
[HttpPost("/team/share-tactic")]
public async Task<IActionResult> ShareTactic([FromBody] ShareTacticToTeamRequest sharedTactic)
{
var userId = accessor.CurrentUserId(HttpContext);
var success = await tactics.ShareTactic(sharedTactic.TacticId, null, sharedTactic.TeamId);
return success ? Ok() : BadRequest();
}
[HttpDelete("/tactics/shared/{tacticId:int}/team/{teamId:int}")]
public async Task<IActionResult> UnshareTactic(int tacticId, int teamId)
{
var currentUserId = accessor.CurrentUserId(HttpContext);
var tactic = await tactics.GetTactic(tacticId);
if (tactic == null)
{
return NotFound();
}
if (currentUserId != tactic.OwnerId)
{
return Unauthorized();
}
var success = await tactics.UnshareTactic(tacticId, null, teamId);
return success ? Ok() : NotFound();
}
[HttpGet("/tactics/shared/team/{teamId:int}")]
public async Task<IActionResult> 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();
}
}

@ -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,64 @@ 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<IActionResult> 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();
}
[HttpDelete("/tactics/shared/{tacticId:int}/user/{userId:int}")]
[Authorize]
public async Task<IActionResult> UnshareTactic(int tacticId, int userId)
{
var currentUserId = accessor.CurrentUserId(HttpContext);
var tactic = await tactics.GetTactic(tacticId);
if (tactic == null)
{
return NotFound();
}
if (currentUserId != tactic.OwnerId)
{
return Unauthorized();
}
var success = await tactics.UnshareTactic(tacticId, userId, null);
return success ? Ok() : NotFound();
}
[HttpGet("/tactics/shared/user/{userId:int}")]
[Authorize]
public async Task<IActionResult> 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();
}
}

@ -16,8 +16,9 @@ namespace APIConsole
{
AppContext.AppContext context = new AppContext.AppContext();
ITeamService teams = new DbTeamService(context);
ITacticService tactics = new DbTacticService(context);
IContextAccessor accessor = new HttpContextAccessor();
_controller = new TeamsController(teams, accessor);
_controller = new TeamsController(teams, tactics, accessor);
}
public async void GetMembersOfTest()

@ -13,7 +13,9 @@ public class AppContext : DbContext
public DbSet<TeamEntity> Teams { get; init; }
public DbSet<MemberEntity> Members { get; init; }
public DbSet<TacticStepEntity> TacticSteps { get; set; }
public DbSet<SharedTacticEntity> SharedTactics { get; set; }
public AppContext()
{

@ -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; }
}
}

@ -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);
}
}

@ -159,4 +159,40 @@ public class DbTacticService(AppContext.AppContext context) : ITacticService
return await context.SaveChangesAsync() > 0;
}
public async Task<bool> 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<bool> UnshareTactic(int tacticId, int? userId, int? teamId)
{
SharedTacticEntity? sharedTactic = null;
if (userId.HasValue)
{
sharedTactic = await context.SharedTactics
.FirstOrDefaultAsync(st => st.TacticId == tacticId && st.SharedWithUserId == userId);
}
else if (teamId.HasValue)
{
sharedTactic = await context.SharedTactics
.FirstOrDefaultAsync(st => st.TacticId == tacticId && st.SharedWithTeamId == teamId);
}
if (sharedTactic == null)
{
return false;
}
context.SharedTactics.Remove(sharedTactic);
return await context.SaveChangesAsync() > 0;
}
}

@ -73,6 +73,29 @@ public class DbTeamService(AppContext.AppContext context) : ITeamService
return await context.SaveChangesAsync() > 0;
}
public async Task<IEnumerable<Tactic>> GetSharedTacticsToTeam(int teamId)
{
var sharedTactics = await context.SharedTactics
.Where(st => st.SharedWithTeamId == teamId)
.ToListAsync();
var tactics = new List<Tactic>();
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 Task<IEnumerable<Member>> GetMembersOf(int teamId)
{
@ -131,4 +154,10 @@ public class DbTeamService(AppContext.AppContext context) : ITeamService
? ITeamService.TeamAccessibility.Authorized
: ITeamService.TeamAccessibility.Unauthorized;
}
public async Task<bool> IsUserInTeam(int userId, int teamId)
{
return await context.Members
.AnyAsync(m => m.TeamId == teamId && m.UserId == userId);
}
}

@ -95,4 +95,27 @@ public class DbUserService(AppContext.AppContext context) : IUserService
.FirstOrDefaultAsync(u => u.Email == email))
?.ToModel();
}
public async Task<IEnumerable<Tactic>> GetSharedTacticsToUser(int userId)
{
var sharedTactics = await context.SharedTactics
.Where(st => st.SharedWithUserId == userId)
.ToListAsync();
var tactics = new List<Tactic>();
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;
}
}

@ -0,0 +1,3 @@
namespace Model;
public record SharedTactic(int Id, int TacticId, int? SharedWithUserId, int? SharedWithTeamId);

@ -56,6 +56,10 @@ public interface ITacticService
/// <returns>A task that represents the asynchronous operation. The task result contains the JSON content of the step.</returns>
Task<string?> GetTacticStepContent(int tacticId, int stepId);
public Task<bool> ShareTactic(int tacticId, int? userId, int? teamId);
public Task<bool> UnshareTactic(int tacticId, int? userId, int? teamId);
/// <summary>
/// Retrieves the root step of the specified tactic.
/// </summary>
@ -93,4 +97,5 @@ public interface ITacticService
/// <param name="stepId">The ID of the step to remove.</param>
/// <returns>A task that represents the asynchronous operation. The task result contains a boolean indicating whether the removal was successful.</returns>
Task<bool> RemoveTacticStep(int tacticId, int stepId);
}

@ -75,6 +75,11 @@ public interface ITeamService
*/
Authorized
}
public Task<IEnumerable<Tactic>> GetSharedTacticsToTeam(int teamId);
public Task<bool> IsUserInTeam(int userId, int teamId);
/**
* Ensures that the given user identifier van perform an operation that requires the given role permission.
@ -82,4 +87,5 @@ public interface ITeamService
* given team.
*/
public Task<TeamAccessibility> EnsureAccessibility(int userId, int teamId, MemberRole role);
}

@ -46,6 +46,10 @@ public interface IUserService
/// Updates an existing user.
/// </summary>
Task UpdateUser(User user);
public Task<IEnumerable<Tactic>> GetSharedTacticsToUser(int userId);
/// <summary>
/// Authorizes a user with the specified email and password.

@ -53,6 +53,14 @@ public class StubAppContext(DbContextOptions<AppContext> options) : AppContext(o
TacticId = 1,
ParentId = null
});
builder.Entity<SharedTacticEntity>()
.HasData(new SharedTacticEntity
{
Id = 1,
TacticId = 1,
SharedWithUserId = 2
});
builder.Entity<TeamEntity>()

@ -21,7 +21,7 @@ public class TeamsControllerTest
);
context.Database.EnsureCreated();
var controller = new TeamsController(
new DbTeamService(context),
new DbTeamService(context), new DbTacticService(context),
new ManualContextAccessor(userId)
);

@ -1,7 +1,7 @@
using API.Controllers;
using API.DTO;
using DbServices;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using Model;
@ -50,4 +50,37 @@ public class UsersControllerTest
// [new TacticDto(1, "New tactic", 1, "PLAIN", 1717106400000L)]
// ));
}
[Fact]
public async Task ShareTacticTest()
{
var controller = GetUserController(1);
var result = await controller.ShareTactic(new UsersController.ShareTacticToUserRequest(1, 2));
result.Should().BeOfType<OkResult>();
}
[Fact]
public async Task GetSharedTacticsToUserTest()
{
var controller = GetUserController(2);
var result = await controller.GetSharedTacticsToUser(2);
var okResult = result as OkObjectResult;
var sharedTactics = okResult.Value as IEnumerable<Tactic>;
sharedTactics.Should().NotBeNull();
sharedTactics.Should().ContainSingle();
var tactic = sharedTactics.First();
tactic.Id.Should().Be(1);
}
[Fact]
public async Task UnshareTacticTest()
{
var controller = GetUserController(1);
var result = await controller.UnshareTactic(1, 2);
result.Should().BeOfType<OkResult>();
}
}
Loading…
Cancel
Save