mirror of
https://github.com/Govor-team/Govor.git
synced 2026-07-21 19:54:55 +00:00
app.MapHub<FriendsHub>("/api/friends");
This commit is contained in:
@@ -8,6 +8,7 @@ COPY Govor.API/*.csproj ./Govor.API/
|
||||
COPY Govor.Application/*.csproj ./Govor.Application/
|
||||
COPY Govor.Core/*.csproj ./Govor.Core/
|
||||
COPY Govor.Data/*.csproj ./Govor.Data/
|
||||
COPY Govor.Contracts/*.csproj ./Govor.Contracts/
|
||||
RUN dotnet restore
|
||||
|
||||
# Копируем все исходники и билдим проект в Release режиме
|
||||
|
||||
@@ -14,52 +14,52 @@ namespace Govor.API.Tests.IntegrationTests.Controllers;
|
||||
[TestFixture]
|
||||
public class FriendsControllerTests
|
||||
{
|
||||
/*
|
||||
private Fixture _fixture;
|
||||
private Mock<ILogger<FriendsController>> _loggerMock;
|
||||
private Mock<IFriendsService> _friendsServiceMock;
|
||||
private Mock<ICurrentUserService> _currentUserServiceMock;
|
||||
private FriendsController _controller;
|
||||
|
||||
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
_fixture = new Fixture();
|
||||
_fixture.Behaviors.OfType<ThrowingRecursionBehavior>().ToList().ForEach(b => _fixture.Behaviors.Remove(b));
|
||||
_fixture.Behaviors.Add(new OmitOnRecursionBehavior());
|
||||
|
||||
|
||||
_loggerMock = new Mock<ILogger<FriendsController>>();
|
||||
_friendsServiceMock = new Mock<IFriendsService>();
|
||||
_currentUserServiceMock = new Mock<ICurrentUserService>();
|
||||
|
||||
|
||||
_controller = new FriendsController(
|
||||
_loggerMock.Object,
|
||||
_friendsServiceMock.Object,
|
||||
_currentUserServiceMock.Object
|
||||
);
|
||||
}
|
||||
|
||||
// Tests for Search action
|
||||
// Tests for Search action
|
||||
[Test]
|
||||
public async Task Search_ValidRequest_ReturnsOkResult()
|
||||
{
|
||||
var users = _fixture.CreateMany<User>().ToList();
|
||||
var userId = _fixture.Create<Guid>();
|
||||
var query = _fixture.Create<string>();
|
||||
|
||||
|
||||
_currentUserServiceMock.Setup(c => c.GetCurrentUserId()).Returns(userId);
|
||||
|
||||
|
||||
_friendsServiceMock.Setup(f => f.SearchUsersAsync(query, userId))
|
||||
.ReturnsAsync(users);
|
||||
|
||||
// Act
|
||||
|
||||
// Act
|
||||
var result = await _controller.Search(query);
|
||||
|
||||
|
||||
var okResult = result as OkObjectResult;
|
||||
dynamic value = okResult.Value;
|
||||
|
||||
|
||||
List<UserDto> userDtos = value as List<UserDto>;
|
||||
|
||||
// Assert
|
||||
|
||||
// Assert
|
||||
Assert.That(value, Is.Not.Null);
|
||||
Assert.That(value.Count, Is.EqualTo(users.Count));
|
||||
Assert.That(userDtos.Select(u => u.Id), Is.EqualTo(users.Select(u => u.Id)));
|
||||
@@ -68,43 +68,43 @@ public class FriendsControllerTests
|
||||
[Test]
|
||||
public async Task Search_InvalidQuery_BadRequest()
|
||||
{
|
||||
// Act
|
||||
// Act
|
||||
var result = await _controller.Search(string.Empty);
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<BadRequestObjectResult>());
|
||||
var badRequestResult = result as BadRequestObjectResult;
|
||||
Assert.That(badRequestResult.Value, Is.EqualTo("Query cannot be empty"));
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task Search_NotFound_IfThrowsSearchUsersException()
|
||||
{
|
||||
// Arrange
|
||||
// Arrange
|
||||
_friendsServiceMock.Setup(f => f.SearchUsersAsync(It.IsAny<string>(), It.IsAny<Guid>()))
|
||||
.ThrowsAsync(new SearchUsersException(_fixture.Create<string>()));
|
||||
|
||||
// Act
|
||||
|
||||
// Act
|
||||
var result = await _controller.Search(_fixture.Create<string>());
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<NotFoundObjectResult>());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task Search_StatusCode500_IfThrowsSomeException()
|
||||
{
|
||||
// Arrange
|
||||
// Arrange
|
||||
_friendsServiceMock.Setup(f => f.SearchUsersAsync(It.IsAny<string>(), It.IsAny<Guid>()))
|
||||
.ThrowsAsync(new Exception(_fixture.Create<string>()));
|
||||
|
||||
// Act
|
||||
|
||||
// Act
|
||||
var result = await _controller.Search(_fixture.Create<string>());
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<ObjectResult>());
|
||||
var objectResult = result as ObjectResult;
|
||||
Assert.That(objectResult.StatusCode, Is.EqualTo(500));
|
||||
}
|
||||
|
||||
|
||||
// Test for SendRequest action
|
||||
[Test]
|
||||
public async Task SendRequest_ValidRequest_ReturnsOk()
|
||||
@@ -122,66 +122,66 @@ public class FriendsControllerTests
|
||||
public async Task SendRequest_Throws_InvalidOperationException_ReturnsUnprocessableEntity()
|
||||
{
|
||||
var targetUserId = Guid.NewGuid();
|
||||
|
||||
|
||||
_currentUserServiceMock.Setup(c => c.GetCurrentUserId()).Returns(targetUserId);
|
||||
|
||||
|
||||
_friendsServiceMock.Setup(f => f.SendFriendRequestAsync(targetUserId, targetUserId))
|
||||
.ThrowsAsync(new InvalidOperationException());
|
||||
|
||||
|
||||
var result = await _controller.SendRequest(targetUserId);
|
||||
|
||||
Assert.That(result, Is.InstanceOf<UnprocessableEntityObjectResult>());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task SendRequest_Throws_RequestAlreadySentException_ReturnsConflict()
|
||||
{
|
||||
var targetUserId = Guid.NewGuid();
|
||||
var currentUserId = Guid.NewGuid();
|
||||
|
||||
|
||||
_currentUserServiceMock.Setup(c => c.GetCurrentUserId()).Returns(currentUserId);
|
||||
|
||||
|
||||
_friendsServiceMock.Setup(f => f.SendFriendRequestAsync(currentUserId, targetUserId))
|
||||
.ThrowsAsync(new RequestAlreadySentException(currentUserId, targetUserId));
|
||||
|
||||
|
||||
var result = await _controller.SendRequest(targetUserId);
|
||||
|
||||
Assert.That(result, Is.InstanceOf<ConflictObjectResult>());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task SendRequest_StatusCode500_IfThrowsSomeException()
|
||||
{
|
||||
// Arrange
|
||||
// Arrange
|
||||
_friendsServiceMock.Setup(f => f.SendFriendRequestAsync(It.IsAny<Guid>(), It.IsAny<Guid>()))
|
||||
.ThrowsAsync(new Exception(_fixture.Create<string>()));
|
||||
|
||||
// Act
|
||||
|
||||
// Act
|
||||
var result = await _controller.Search(_fixture.Create<string>());
|
||||
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<ObjectResult>());
|
||||
var objectResult = result as ObjectResult;
|
||||
Assert.That(objectResult.StatusCode, Is.EqualTo(500));
|
||||
}
|
||||
|
||||
// Tests for GetIncomingRequests action
|
||||
|
||||
// Tests for GetIncomingRequests action
|
||||
[Test]
|
||||
public async Task GetIncomingRequests_ValidRequest_ReturnsOkResult()
|
||||
{
|
||||
// Arrange
|
||||
var currentId = _fixture.Create<Guid>();
|
||||
var friendships = _fixture.CreateMany<Friendship>().ToList();
|
||||
|
||||
|
||||
_currentUserServiceMock.Setup(c => c.GetCurrentUserId())
|
||||
.Returns(currentId);
|
||||
|
||||
|
||||
_friendsServiceMock.Setup(f => f.GetIncomingRequestsAsync(currentId))
|
||||
.ReturnsAsync(friendships);
|
||||
// Act
|
||||
// Act
|
||||
var result = await _controller.GetIncomingRequests();
|
||||
|
||||
// Assert
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<OkObjectResult>());
|
||||
var okResult = result as OkObjectResult;
|
||||
List<FriendshipDto> value = okResult.Value as List<FriendshipDto>;
|
||||
@@ -194,17 +194,17 @@ public class FriendsControllerTests
|
||||
{
|
||||
// Arrange
|
||||
var currentId = _fixture.Create<Guid>();
|
||||
|
||||
|
||||
_currentUserServiceMock.Setup(c => c.GetCurrentUserId())
|
||||
.Returns(currentId);
|
||||
|
||||
|
||||
_friendsServiceMock.Setup(f => f.GetIncomingRequestsAsync(currentId))
|
||||
.ThrowsAsync(new InvalidOperationException());
|
||||
|
||||
// Act
|
||||
|
||||
// Act
|
||||
var result = await _controller.GetIncomingRequests();
|
||||
|
||||
// Assert
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<BadRequestObjectResult>());
|
||||
var badRequestResult = result as BadRequestObjectResult;
|
||||
dynamic value = badRequestResult.Value;
|
||||
@@ -216,169 +216,171 @@ public class FriendsControllerTests
|
||||
{
|
||||
// Arrange
|
||||
var currentId = _fixture.Create<Guid>();
|
||||
|
||||
|
||||
_currentUserServiceMock.Setup(c => c.GetCurrentUserId())
|
||||
.Returns(currentId);
|
||||
|
||||
|
||||
_friendsServiceMock.Setup(f => f.GetIncomingRequestsAsync(currentId))
|
||||
.ThrowsAsync(new Exception());
|
||||
|
||||
// Act
|
||||
|
||||
// Act
|
||||
var result = await _controller.GetIncomingRequests();
|
||||
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<ObjectResult>());
|
||||
var objectResult = result as ObjectResult;
|
||||
Assert.That(objectResult.StatusCode, Is.EqualTo(500));
|
||||
}
|
||||
|
||||
// Tests for AcceptFriend action
|
||||
|
||||
// Tests for AcceptFriend action
|
||||
[Test]
|
||||
public async Task AcceptFriend_ValidRequest_ReturnsOkResult()
|
||||
{
|
||||
// Arrange
|
||||
// Arrange
|
||||
var currentId = Guid.NewGuid();
|
||||
var targetUserId = Guid.NewGuid();
|
||||
|
||||
|
||||
_currentUserServiceMock.Setup(c => c.GetCurrentUserId())
|
||||
.Returns(currentId);
|
||||
// Act
|
||||
// Act
|
||||
var result = await _controller.AcceptFriend(targetUserId);
|
||||
|
||||
// Assert
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<OkObjectResult>());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task AcceptFriend_InvalidOperationException_ReturnsNotFound()
|
||||
{
|
||||
// Arrange
|
||||
// Arrange
|
||||
var currentId = Guid.NewGuid();
|
||||
var targetUserId = Guid.NewGuid();
|
||||
|
||||
|
||||
_currentUserServiceMock.Setup(c => c.GetCurrentUserId())
|
||||
.Returns(currentId);
|
||||
|
||||
|
||||
_friendsServiceMock.Setup(f => f.AcceptFriendRequestAsync(targetUserId, currentId))
|
||||
.ThrowsAsync(new InvalidOperationException());
|
||||
|
||||
// Act
|
||||
|
||||
// Act
|
||||
var result = await _controller.AcceptFriend(targetUserId);
|
||||
|
||||
// Assert
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<NotFoundObjectResult>());
|
||||
}
|
||||
[Test]
|
||||
public async Task AcceptFriend_UnauthorizedAccessException_ReturnsForbid()
|
||||
{
|
||||
// Arrange
|
||||
// Arrange
|
||||
var currentId = Guid.NewGuid();
|
||||
var targetUserId = Guid.NewGuid();
|
||||
|
||||
|
||||
_currentUserServiceMock.Setup(c => c.GetCurrentUserId())
|
||||
.Returns(currentId);
|
||||
|
||||
|
||||
_friendsServiceMock.Setup(f => f.AcceptFriendRequestAsync(targetUserId, currentId))
|
||||
.ThrowsAsync(new UnauthorizedAccessException());
|
||||
|
||||
// Act
|
||||
|
||||
// Act
|
||||
var result = await _controller.AcceptFriend(targetUserId);
|
||||
|
||||
// Assert
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<ForbidResult>());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task AcceptFriend_Exception_ReturnsStatusCode500()
|
||||
{
|
||||
// Arrange
|
||||
// Arrange
|
||||
var currentId = Guid.NewGuid();
|
||||
var targetUserId = Guid.NewGuid();
|
||||
|
||||
|
||||
_currentUserServiceMock.Setup(c => c.GetCurrentUserId())
|
||||
.Returns(currentId);
|
||||
|
||||
|
||||
_friendsServiceMock.Setup(f => f.AcceptFriendRequestAsync(targetUserId, currentId))
|
||||
.ThrowsAsync(new Exception());
|
||||
|
||||
// Act
|
||||
|
||||
// Act
|
||||
var result = await _controller.AcceptFriend(targetUserId);
|
||||
|
||||
// Assert
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<ObjectResult>());
|
||||
var objectResult = result as ObjectResult;
|
||||
Assert.That(objectResult.StatusCode, Is.EqualTo(500));
|
||||
}
|
||||
|
||||
// Tests for GetFriends action
|
||||
|
||||
// Tests for GetFriends action
|
||||
[Test]
|
||||
public async Task GetFriends_ValidRequest_ReturnsOkResult()
|
||||
{
|
||||
// Arrange
|
||||
// Arrange
|
||||
var currentId = _fixture.Create<Guid>();
|
||||
var users = _fixture.CreateMany<User>().ToList();
|
||||
|
||||
|
||||
_currentUserServiceMock.Setup(c => c.GetCurrentUserId())
|
||||
.Returns(currentId);
|
||||
|
||||
_friendsServiceMock.Setup(f => f.GetFriendsAsync(currentId))
|
||||
.ReturnsAsync(users);
|
||||
// Act
|
||||
// Act
|
||||
var result = await _controller.GetFriends();
|
||||
|
||||
// Assert
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<OkObjectResult>());
|
||||
var okResult = result as OkObjectResult;
|
||||
List<UserDto> value = okResult.Value as List<UserDto>;
|
||||
Assert.That(value, Is.Not.Null);
|
||||
Assert.That(value.Count, Is.EqualTo(users.Count));
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task GetFriends_InvalidOperationException_ReturnsBadRequest()
|
||||
{
|
||||
// Arrange
|
||||
// Arrange
|
||||
var currentId = _fixture.Create<Guid>();
|
||||
var users = _fixture.CreateMany<User>().ToList();
|
||||
|
||||
|
||||
_currentUserServiceMock.Setup(c => c.GetCurrentUserId())
|
||||
.Returns(currentId);
|
||||
|
||||
_friendsServiceMock.Setup(f => f.GetFriendsAsync(currentId))
|
||||
.ThrowsAsync(new InvalidOperationException());
|
||||
|
||||
// Act
|
||||
|
||||
// Act
|
||||
var result = await _controller.GetFriends();
|
||||
|
||||
// Assert
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<OkObjectResult>());
|
||||
}
|
||||
|
||||
|
||||
[Test]
|
||||
public async Task GetFriends_Exception_ReturnsStatusCode500()
|
||||
{
|
||||
// Arrange
|
||||
// Arrange
|
||||
var currentId = _fixture.Create<Guid>();
|
||||
var users = _fixture.CreateMany<User>().ToList();
|
||||
|
||||
|
||||
_currentUserServiceMock.Setup(c => c.GetCurrentUserId())
|
||||
.Returns(currentId);
|
||||
|
||||
_friendsServiceMock.Setup(f => f.GetFriendsAsync(currentId))
|
||||
.ThrowsAsync(new Exception());
|
||||
|
||||
// Act
|
||||
|
||||
// Act
|
||||
var result = await _controller.GetFriends();
|
||||
|
||||
// Assert
|
||||
|
||||
// Assert
|
||||
Assert.That(result, Is.InstanceOf<ObjectResult>());
|
||||
var objectResult = result as ObjectResult;
|
||||
Assert.That(objectResult.StatusCode, Is.EqualTo(500));
|
||||
}
|
||||
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
_controller.Dispose();
|
||||
}
|
||||
*/
|
||||
}
|
||||
@@ -1,212 +0,0 @@
|
||||
using Govor.Application.Exceptions.FriendsService;
|
||||
using Govor.Application.Interfaces;
|
||||
using Govor.Application.Interfaces.Infrastructure.Extensions;
|
||||
using Govor.Contracts.DTOs;
|
||||
using Govor.Core.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace Govor.API.Controllers;
|
||||
|
||||
[ApiController]
|
||||
[Route("api/[controller]")]
|
||||
[Authorize(Roles = "User,Admin")]
|
||||
public class FriendsController : Controller
|
||||
{
|
||||
private readonly ILogger<FriendsController> _logger;
|
||||
private readonly IFriendsService _friendsService;
|
||||
private readonly ICurrentUserService _currentUserService;
|
||||
|
||||
public FriendsController(ILogger<FriendsController> logger,
|
||||
IFriendsService friendsService,
|
||||
ICurrentUserService currentUserService)
|
||||
{
|
||||
_logger = logger;
|
||||
_friendsService = friendsService;
|
||||
_currentUserService = currentUserService;
|
||||
}
|
||||
|
||||
[HttpGet("search")] // api/friends/search?query=
|
||||
public async Task<IActionResult> Search(string query)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(query))
|
||||
return BadRequest("Query cannot be empty");
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _friendsService.SearchUsersAsync(query, _currentUserService.GetCurrentUserId());
|
||||
return Ok(BuildUserDtos(result));
|
||||
}
|
||||
catch (SearchUsersException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, ex.Message);
|
||||
return NotFound(new { error = ex.Message });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return StatusCode(500, new { error = "Internal error during user search." });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("request")]
|
||||
public async Task<IActionResult> SendRequest(Guid targetUserId)
|
||||
{
|
||||
if (targetUserId == Guid.Empty)
|
||||
return BadRequest("Target user ID is invalid");
|
||||
|
||||
try
|
||||
{
|
||||
await _friendsService.SendFriendRequestAsync(_currentUserService.GetCurrentUserId(), targetUserId);
|
||||
return Ok(new { message = "Friend request sent successfully." });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Tried to send request to self");
|
||||
return UnprocessableEntity(new { error = ex.Message });
|
||||
}
|
||||
catch (RequestAlreadySentException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, ex.Message);
|
||||
return Conflict(new { error = ex.Message });
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return StatusCode(500, new { error = "Failed to send friend request." });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("requests")]
|
||||
public async Task<IActionResult> GetIncomingRequests()
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _friendsService.GetIncomingRequestsAsync(_currentUserService.GetCurrentUserId());
|
||||
return Ok(BuildFriendshipDtos(result));
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return BadRequest("Failed to get friend requests. User data missing.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return StatusCode(500, new { error = "Internal server error." });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("responses")]
|
||||
public async Task<IActionResult> GetResponses()
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _friendsService.GetResponsesAsync(_currentUserService.GetCurrentUserId());
|
||||
return Ok(BuildFriendshipDtos(result));
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return Ok(Array.Empty<FriendshipDto>());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return StatusCode(500, new { error = "Internal server error." });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("accept")]
|
||||
public async Task<IActionResult> AcceptFriend([FromQuery] Guid friendshipId)
|
||||
{
|
||||
if (friendshipId == Guid.Empty)
|
||||
return BadRequest("Requester ID is invalid");
|
||||
|
||||
try
|
||||
{
|
||||
await _friendsService.AcceptFriendRequestAsync(friendshipId, _currentUserService.GetCurrentUserId());
|
||||
return Ok(new { message = "Friend request accepted." });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, ex.Message);
|
||||
return NotFound(new { error = ex.Message });
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, ex.Message);
|
||||
return Forbid();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return StatusCode(500, new { error = "Failed to accept friend request." });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("reject")]
|
||||
public async Task<IActionResult> RejectFriend([FromQuery] Guid friendshipId)
|
||||
{
|
||||
if (friendshipId == Guid.Empty)
|
||||
return BadRequest("Requester ID is invalid");
|
||||
|
||||
try
|
||||
{
|
||||
await _friendsService.RejectFriendRequestAsync(friendshipId, _currentUserService.GetCurrentUserId());
|
||||
return Ok(new { message = "Friend request rejected." });
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, ex.Message);
|
||||
return NotFound(new { error = ex.Message });
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, ex.Message);
|
||||
return Forbid();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return StatusCode(500, new { error = "Failed to accept friend request." });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public async Task<IActionResult> GetFriends()
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _friendsService.GetFriendsAsync(_currentUserService.GetCurrentUserId());
|
||||
return Ok(BuildUserDtos(result));
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return Ok(Array.Empty<UserDto>());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return StatusCode(500, new { error = "Internal server error." });
|
||||
}
|
||||
}
|
||||
|
||||
private List<UserDto> BuildUserDtos(IEnumerable<User> users) => users.Select(user => new UserDto
|
||||
{
|
||||
Id = user.Id,
|
||||
Username = user.Username,
|
||||
Description = user.Description,
|
||||
WasOnline = user.WasOnline,
|
||||
IconId = user.IconId
|
||||
}).ToList();
|
||||
|
||||
private List<FriendshipDto> BuildFriendshipDtos(IEnumerable<Friendship> friendships) => friendships.Select(f => new FriendshipDto
|
||||
{
|
||||
Id = f.Id,
|
||||
Status = f.Status,
|
||||
AddresseeId = f.AddresseeId,
|
||||
RequesterId = f.RequesterId,
|
||||
}).ToList();
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
using Govor.Application.Interfaces.Friends;
|
||||
using Govor.Application.Interfaces.Infrastructure.Extensions;
|
||||
using Govor.Contracts.DTOs;
|
||||
using Govor.Core.Models;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
@@ -10,11 +12,11 @@ namespace Govor.API.Controllers.Friends;
|
||||
[Route("api/friends")]
|
||||
public class FriendsRequestQueryController : Controller
|
||||
{
|
||||
private readonly ILogger<FriendsController> _logger;
|
||||
private readonly ILogger<FriendsRequestQueryController> _logger;
|
||||
private readonly IFriendRequestQueryService _friendsService;
|
||||
private readonly ICurrentUserService _currentUserService;
|
||||
|
||||
public FriendsRequestQueryController(ILogger<FriendsController> logger,
|
||||
public FriendsRequestQueryController(ILogger<FriendsRequestQueryController> logger,
|
||||
IFriendRequestQueryService friendsService,
|
||||
ICurrentUserService currentUserService)
|
||||
{
|
||||
@@ -23,5 +25,51 @@ public class FriendsRequestQueryController : Controller
|
||||
_currentUserService = currentUserService;
|
||||
}
|
||||
|
||||
[HttpGet("requests")] // api/friends/requests
|
||||
public async Task<IActionResult> GetIncomingRequests()
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _friendsService.GetIncomingAsync(_currentUserService.GetCurrentUserId());
|
||||
return Ok(BuildFriendshipDtos(result));
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return BadRequest("Failed to get friend requests. User data missing.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return StatusCode(500, new { error = "Internal server error." });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("responses")]// api/friends/responses
|
||||
public async Task<IActionResult> GetResponses()
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _friendsService.GetResponsesAsync(_currentUserService.GetCurrentUserId());
|
||||
return Ok(BuildFriendshipDtos(result));
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return Ok(Array.Empty<FriendshipDto>());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return StatusCode(500, new { error = "Internal server error." });
|
||||
}
|
||||
}
|
||||
|
||||
private List<FriendshipDto> BuildFriendshipDtos(IEnumerable<Friendship> friendships) => friendships.Select(f => new FriendshipDto
|
||||
{
|
||||
Id = f.Id,
|
||||
Status = f.Status,
|
||||
AddresseeId = f.AddresseeId,
|
||||
RequesterId = f.RequesterId,
|
||||
}).ToList();
|
||||
}
|
||||
@@ -10,12 +10,12 @@ namespace Govor.API.Controllers.Friends;
|
||||
[Route("api/friends")]
|
||||
public class FriendshipController : Controller
|
||||
{
|
||||
private readonly ILogger<FriendsController> _logger;
|
||||
private readonly ILogger<FriendshipController> _logger;
|
||||
private readonly IFriendshipService _friendsService;
|
||||
//private readonly IUserDtoBuilder _builder;
|
||||
private readonly ICurrentUserService _currentUserService;
|
||||
|
||||
public FriendshipController(ILogger<FriendsController> logger,
|
||||
public FriendshipController(ILogger<FriendshipController> logger,
|
||||
IFriendshipService friendsService,
|
||||
ICurrentUserService currentUserService)
|
||||
{
|
||||
@@ -47,6 +47,26 @@ public class FriendshipController : Controller
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet] // api/friends
|
||||
public async Task<IActionResult> GetFriends()
|
||||
{
|
||||
try
|
||||
{
|
||||
var result = await _friendsService.GetFriendsAsync(_currentUserService.GetCurrentUserId());
|
||||
return Ok(BuildUserDtos(result));
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return Ok(Array.Empty<UserDto>());
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, ex.Message);
|
||||
return StatusCode(500, new { error = "Internal server error." });
|
||||
}
|
||||
}
|
||||
|
||||
private List<UserDto> BuildUserDtos(IEnumerable<User> users) => users.Select(user => new UserDto
|
||||
{
|
||||
Id = user.Id,
|
||||
|
||||
@@ -11,15 +11,61 @@ public class FriendsHub : Hub
|
||||
private readonly ILogger<FriendsHub> _logger;
|
||||
private readonly IFriendRequestCommandService _friendRequestService;
|
||||
private readonly ICurrentUserService _currentUserService;
|
||||
|
||||
|
||||
public FriendsHub(IFriendRequestCommandService friendRequestService, ICurrentUserService currentUserService, ILogger<FriendsHub> logger)
|
||||
{
|
||||
_friendRequestService = friendRequestService;
|
||||
_currentUserService = currentUserService;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public override async Task OnConnectedAsync()
|
||||
{
|
||||
var userId = GetUserId();
|
||||
if (userId == Guid.Empty)
|
||||
{
|
||||
_logger.LogWarning("User connected with invalid UserID claim.");
|
||||
Context.Abort(); // Abort connection if userID is invalid
|
||||
return;
|
||||
}
|
||||
|
||||
public async Task<HubResult<object>> SendRequest(Guid targetUserId)
|
||||
// Add user to their own group (for private messages and notifications)
|
||||
await Groups.AddToGroupAsync(Context.ConnectionId, userId.ToString());
|
||||
_logger.LogInformation("User {UserId} connected with ConnectionId {ConnectionId} and added to their group",
|
||||
userId, Context.ConnectionId);
|
||||
|
||||
|
||||
await base.OnConnectedAsync();
|
||||
}
|
||||
public override async Task OnDisconnectedAsync(Exception? exception)
|
||||
{
|
||||
var userId =
|
||||
GetUserId(suppressException: true); // Suppress exception if userID is not found (e.g. connection aborted early)
|
||||
if (userId != Guid.Empty)
|
||||
{
|
||||
// Remove user from their own group
|
||||
await Groups.RemoveFromGroupAsync(Context.ConnectionId, userId.ToString());
|
||||
_logger.LogInformation(
|
||||
"User {UserId} disconnected with ConnectionId {ConnectionId} and removed from their group", userId,
|
||||
Context.ConnectionId);
|
||||
}
|
||||
else if (exception != null)
|
||||
{
|
||||
_logger.LogWarning(exception,
|
||||
"User disconnected with an exception and invalid UserID claim. ConnectionId: {ConnectionId}",
|
||||
Context.ConnectionId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger.LogInformation(
|
||||
"User disconnected with no exception and invalid UserID claim. ConnectionId: {ConnectionId}",
|
||||
Context.ConnectionId);
|
||||
}
|
||||
|
||||
await base.OnDisconnectedAsync(exception);
|
||||
}
|
||||
|
||||
public async Task<HubResult<object>> SendRequest(Guid targetUserId)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -110,4 +156,21 @@ public class FriendsHub : Hub
|
||||
return HubResult<object>.Error("Unexpected error! Please try later!");
|
||||
}
|
||||
}
|
||||
|
||||
private Guid GetUserId(bool suppressException = false)
|
||||
{
|
||||
var userIdClaim = Context.User?.FindFirst("userId")?.Value;
|
||||
if (string.IsNullOrEmpty(userIdClaim) || !Guid.TryParse(userIdClaim, out var userId))
|
||||
{
|
||||
if (!suppressException)
|
||||
{
|
||||
_logger.LogError("Could not retrieve sender userId. Claim was: {UserIDClaim}", userIdClaim);
|
||||
throw new UnauthorizedAccessException("userID claim is missing or invalid.");
|
||||
}
|
||||
|
||||
return Guid.Empty;
|
||||
}
|
||||
|
||||
return userId;
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,9 @@ app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.MapControllers();
|
||||
|
||||
app.MapHub<ChatsHub>("/api/chats");
|
||||
app.MapHub<FriendsHub>("/api/friends");
|
||||
|
||||
app.MapSwagger().RequireAuthorization();
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace Govor.ConsoleClient
|
||||
//static string baseUrl = "https://govor-team-govor-88b3.twc1.net";
|
||||
static string baseUrl = "https://localhost:7155";
|
||||
static string? AuthToken = null;
|
||||
static HttpClientService HttpService = new(baseUrl); // поменяй URL на свой
|
||||
static HttpClientService HttpService = new(baseUrl);
|
||||
private static FriendsClient? friendsClient;
|
||||
static HubConnection? _hubConnection;
|
||||
static Dictionary<string, List<string>> ChatHistory = new();
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<LangVersion>latest</LangVersion>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
Reference in New Issue
Block a user