mirror of
https://github.com/Govor-team/Govor.git
synced 2026-07-21 19:54:55 +00:00
Created SessionController and UserSessionReader
+ tests for UserSessionReader
This commit is contained in:
@@ -87,6 +87,8 @@ public static class ConfigurationProgramExtensions
|
|||||||
services.AddAutoMapper(typeof(MappingProfile));
|
services.AddAutoMapper(typeof(MappingProfile));
|
||||||
|
|
||||||
services.AddScoped<IHubUserAccessor, HubUserAccessor>();
|
services.AddScoped<IHubUserAccessor, HubUserAccessor>();
|
||||||
|
|
||||||
|
services.AddScoped<IUserSessionReader, UserSessionReader>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddRepositories(this IServiceCollection services)
|
public static void AddRepositories(this IServiceCollection services)
|
||||||
|
|||||||
@@ -21,5 +21,7 @@ public class MappingProfile : Profile
|
|||||||
.AfterMap<UserToUserDtoMappingAction>();
|
.AfterMap<UserToUserDtoMappingAction>();
|
||||||
|
|
||||||
CreateMap<Friendship, FriendshipDto>();
|
CreateMap<Friendship, FriendshipDto>();
|
||||||
|
|
||||||
|
CreateMap<UserSession, SessionDto>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
using AutoMapper;
|
||||||
|
using Govor.Application.Interfaces.Infrastructure.Extensions;
|
||||||
|
using Govor.Application.Interfaces.UserSession;
|
||||||
|
using Govor.Contracts.DTOs;
|
||||||
|
using Govor.Data.Repositories.Exceptions;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
|
||||||
|
namespace Govor.API.Controllers;
|
||||||
|
|
||||||
|
[ApiController]
|
||||||
|
[Authorize(Roles = "Admin,User")]
|
||||||
|
[Route("api/[controller]")]
|
||||||
|
public class SessionController : Controller
|
||||||
|
{
|
||||||
|
private readonly ILogger<SessionController> _logger;
|
||||||
|
private readonly ICurrentUserService _currentUserService;
|
||||||
|
private readonly IUserSessionReader _userSessionReader;
|
||||||
|
private readonly IUserSessionRevoker _userSessionRevoker;
|
||||||
|
private readonly IMapper _mapper;
|
||||||
|
|
||||||
|
public SessionController(
|
||||||
|
ILogger<SessionController> logger,
|
||||||
|
ICurrentUserService currentUserService,
|
||||||
|
IUserSessionReader userSessionReader,
|
||||||
|
IUserSessionRevoker userSessionRevoker,
|
||||||
|
IMapper mapper)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_currentUserService = currentUserService;
|
||||||
|
_userSessionReader = userSessionReader;
|
||||||
|
_userSessionRevoker = userSessionRevoker;
|
||||||
|
_mapper = mapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("all")]
|
||||||
|
public async Task<IActionResult> GetAllSessions()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var sessions = await _userSessionReader.GetAllSessionsAsync(_currentUserService.GetCurrentUserId());
|
||||||
|
return Ok(_mapper.Map<List<SessionDto>>(sessions));
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, ex.Message);
|
||||||
|
return Forbid(ex.Message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, ex.Message);
|
||||||
|
return StatusCode(500, "Unexpected Error! Please try again later.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("close/{sessionId}")]
|
||||||
|
public async Task<IActionResult> CloseSession(Guid sessionId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (sessionId == Guid.Empty)
|
||||||
|
return BadRequest("Invalid sessionId.");
|
||||||
|
|
||||||
|
await _userSessionRevoker.CloseSessionByIdAsync(sessionId,
|
||||||
|
_currentUserService.GetCurrentUserId());
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (InvalidOperationException ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, ex.Message);
|
||||||
|
return BadRequest(ex.Message);
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, ex.Message);
|
||||||
|
return Forbid(ex.Message);
|
||||||
|
}
|
||||||
|
catch (NotFoundException ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, ex.Message);
|
||||||
|
return NotFound(ex.Message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, ex.Message);
|
||||||
|
return StatusCode(500, "Unexpected Error! Please try again later.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpDelete("close/all")]
|
||||||
|
public async Task<IActionResult> CloseAllSessions()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await _userSessionRevoker.CloseAllSessionsAsync(_currentUserService.GetCurrentUserId());
|
||||||
|
return Ok();
|
||||||
|
}
|
||||||
|
catch (UnauthorizedAccessException ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning(ex, ex.Message);
|
||||||
|
return Forbid(ex.Message);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, ex.Message);
|
||||||
|
return StatusCode(500, "Unexpected Error! Please try again later.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
using AutoFixture;
|
||||||
|
using Govor.Application.Interfaces.UserSession;
|
||||||
|
using Govor.Application.Services.UserSessions;
|
||||||
|
using Govor.Core.Models;
|
||||||
|
using Govor.Core.Repositories.UserSessionsRepository;
|
||||||
|
using Govor.Data.Repositories.Exceptions;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Moq;
|
||||||
|
|
||||||
|
namespace Govor.Application.Tests.Services.UserSessions;
|
||||||
|
|
||||||
|
[TestFixture]
|
||||||
|
[TestOf(typeof(UserSessionReader))]
|
||||||
|
public class UserSessionReaderTests
|
||||||
|
{
|
||||||
|
private Mock<IUserSessionsRepository> _mockUserSessionsRepository;
|
||||||
|
private Mock<ILogger<UserSessionReader>> _mockLogger;
|
||||||
|
private Fixture _fixture;
|
||||||
|
private IUserSessionReader _userSessionReader;
|
||||||
|
|
||||||
|
[SetUp]
|
||||||
|
public void SetUp()
|
||||||
|
{
|
||||||
|
_fixture = new Fixture();
|
||||||
|
|
||||||
|
_mockUserSessionsRepository = new Mock<IUserSessionsRepository>();
|
||||||
|
_mockLogger = new Mock<ILogger<UserSessionReader>>();
|
||||||
|
|
||||||
|
_userSessionReader = new UserSessionReader(_mockUserSessionsRepository.Object,
|
||||||
|
_mockLogger.Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task GetAllUserSessionsAsync_ShouldReturnAllUserSessions()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var sessios = _fixture.CreateMany<UserSession>().ToList();
|
||||||
|
var userId = Guid.NewGuid();
|
||||||
|
|
||||||
|
_mockUserSessionsRepository.Setup(f => f.GetByUserIdAsync(userId))
|
||||||
|
.ReturnsAsync(sessios);
|
||||||
|
// Act
|
||||||
|
var result = await _userSessionReader.GetAllSessionsAsync(userId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.That(result, Is.Not.Null);
|
||||||
|
Assert.That(result, Is.EquivalentTo(sessios));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public async Task GetAllUserSessionsAsync_ThrowsNotFoundByKeyException_ReturnsEmptyList()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var userId = Guid.NewGuid();
|
||||||
|
|
||||||
|
_mockUserSessionsRepository.Setup(f => f.GetByUserIdAsync(userId))
|
||||||
|
.ThrowsAsync(new NotFoundByKeyException<Guid>(userId));
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var result = await _userSessionReader.GetAllSessionsAsync(userId);
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
Assert.That(result, Is.Not.Null);
|
||||||
|
Assert.That(result, Is.Empty);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,6 @@ namespace Govor.Application.Interfaces.UserSession;
|
|||||||
|
|
||||||
public interface IUserSessionRevoker
|
public interface IUserSessionRevoker
|
||||||
{
|
{
|
||||||
Task CloseSessionByRefreshTokenAsync(string refreshToken);
|
Task CloseSessionByIdAsync(Guid sessionId, Guid userId);
|
||||||
Task CloseAllSessionsAsync(Guid userId);
|
Task CloseAllSessionsAsync(Guid userId);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
using Govor.Application.Interfaces.UserSession;
|
||||||
|
using Govor.Core.Models;
|
||||||
|
using Govor.Core.Repositories.UserSessionsRepository;
|
||||||
|
using Govor.Data.Repositories.Exceptions;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
|
namespace Govor.Application.Services.UserSessions;
|
||||||
|
|
||||||
|
public class UserSessionReader : IUserSessionReader
|
||||||
|
{
|
||||||
|
private readonly IUserSessionsRepository _repository;
|
||||||
|
private readonly ILogger<UserSessionReader> _logger;
|
||||||
|
|
||||||
|
public UserSessionReader(IUserSessionsRepository repository, ILogger<UserSessionReader> logger)
|
||||||
|
{
|
||||||
|
_repository = repository;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<List<UserSession>> GetAllSessionsAsync(Guid userId)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.LogInformation($"Getting all sessions for user {userId}");
|
||||||
|
var sessions = await _repository.GetByUserIdAsync(userId);
|
||||||
|
return sessions;
|
||||||
|
}
|
||||||
|
catch (NotFoundByKeyException<Guid> ex)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("The user has no active sessions.");
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Govor.Contracts.DTOs;
|
||||||
|
|
||||||
|
public record SessionDto
|
||||||
|
{
|
||||||
|
public Guid Id { get; init; }
|
||||||
|
public string DeviceInfo { get; init; } = string.Empty;
|
||||||
|
public DateTime CreatedAt { get; init; }
|
||||||
|
public DateTime ExpiresAt { get; init; }
|
||||||
|
public bool IsRevoked { get; init; }
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user