hot fix for friends controller

This commit is contained in:
Artemy
2025-07-03 19:46:53 +07:00
parent 565d3249e5
commit d170c6270f
16 changed files with 193 additions and 49 deletions
@@ -141,7 +141,7 @@ public class FriendsControllerTests
_currentUserServiceMock.Setup(c => c.GetCurrentUserId()).Returns(currentUserId); _currentUserServiceMock.Setup(c => c.GetCurrentUserId()).Returns(currentUserId);
_friendsServiceMock.Setup(f => f.SendFriendRequestAsync(targetUserId, currentUserId)) _friendsServiceMock.Setup(f => f.SendFriendRequestAsync(currentUserId, targetUserId))
.ThrowsAsync(new RequestAlreadySentException(currentUserId, targetUserId)); .ThrowsAsync(new RequestAlreadySentException(currentUserId, targetUserId));
var result = await _controller.SendRequest(targetUserId); var result = await _controller.SendRequest(targetUserId);
@@ -351,7 +351,7 @@ public class FriendsControllerTests
var result = await _controller.GetFriends(); var result = await _controller.GetFriends();
// Assert // Assert
Assert.That(result, Is.InstanceOf<BadRequestObjectResult>()); Assert.That(result, Is.InstanceOf<OkObjectResult>());
} }
[Test] [Test]
+5 -5
View File
@@ -57,7 +57,7 @@ public class FriendsController : Controller
try try
{ {
await _friendsService.SendFriendRequestAsync(targetUserId, _currentUserService.GetCurrentUserId()); await _friendsService.SendFriendRequestAsync(_currentUserService.GetCurrentUserId(), targetUserId);
return Ok(new { message = "Friend request sent successfully." }); return Ok(new { message = "Friend request sent successfully." });
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
@@ -98,14 +98,14 @@ public class FriendsController : Controller
} }
[HttpPost("accept")] [HttpPost("accept")]
public async Task<IActionResult> AcceptFriend(Guid requesterId) public async Task<IActionResult> AcceptFriend(Guid friendshipId)
{ {
if (requesterId == Guid.Empty) if (friendshipId == Guid.Empty)
return BadRequest("Requester ID is invalid"); return BadRequest("Requester ID is invalid");
try try
{ {
await _friendsService.AcceptFriendRequestAsync(requesterId, _currentUserService.GetCurrentUserId()); await _friendsService.AcceptFriendRequestAsync(friendshipId, _currentUserService.GetCurrentUserId());
return Ok(new { message = "Friend request accepted." }); return Ok(new { message = "Friend request accepted." });
} }
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
@@ -136,7 +136,7 @@ public class FriendsController : Controller
catch (InvalidOperationException ex) catch (InvalidOperationException ex)
{ {
_logger.LogError(ex, ex.Message); _logger.LogError(ex, ex.Message);
return BadRequest(new { error = "User data not found." }); return Ok(Array.Empty<UserDto>());
} }
catch (Exception ex) catch (Exception ex)
{ {
@@ -54,4 +54,10 @@ public class OnlinePingingController : Controller
return StatusCode(500, new { error = "Failed to send friend request." }); return StatusCode(500, new { error = "Failed to send friend request." });
} }
} }
[HttpGet("is-online")]
public async Task<IActionResult> IsOnline(Guid userId)
{
return BadRequest();
}
} }
+41 -32
View File
@@ -1,11 +1,9 @@
using System.Security.Claims; using Govor.API.Services;
using Govor.Application.Interfaces; using Govor.Application.Interfaces.Messages;
using Govor.Application.Interfaces.Messages.Parameters;
using Govor.Contracts.Requests.SignalR; using Govor.Contracts.Requests.SignalR;
using Govor.Core.Models; using Govor.Core.Models;
using Govor.Core.Repositories.Users;
using Govor.Data.Repositories.Exceptions;
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.SignalR; using Microsoft.AspNetCore.SignalR;
namespace Govor.API.Hubs; namespace Govor.API.Hubs;
@@ -13,14 +11,16 @@ namespace Govor.API.Hubs;
[Authorize] [Authorize]
public class ChatsHub : Hub public class ChatsHub : Hub
{ {
private readonly IUsersRepository _usersRepository;
private readonly IVerifyFriendship _verifyFriendship;
private readonly ILogger<ChatsHub> _logger; private readonly ILogger<ChatsHub> _logger;
private readonly IChatService _chatService;
private readonly IGroupService _groupService;
public ChatsHub(IUsersRepository usersRepository, ILogger<ChatsHub> logger) public ChatsHub(ILogger<ChatsHub> logger,
IChatService chatService
)
{ {
_usersRepository = usersRepository;
_logger = logger; _logger = logger;
_chatService = chatService;
} }
public override async Task OnConnectedAsync() public override async Task OnConnectedAsync()
@@ -28,7 +28,7 @@ public class ChatsHub : Hub
var userId = GetUserId(); var userId = GetUserId();
if (userId != Guid.Empty) if (userId != Guid.Empty)
{ {
// Привязываем ConnectionId к UserId // Binding ConnectionId to UserId
await Groups.AddToGroupAsync(Context.ConnectionId, userId.ToString()); await Groups.AddToGroupAsync(Context.ConnectionId, userId.ToString());
_logger.LogInformation("User {UserId} connected with ConnectionId {ConnectionId}", userId, Context.ConnectionId); _logger.LogInformation("User {UserId} connected with ConnectionId {ConnectionId}", userId, Context.ConnectionId);
} }
@@ -46,6 +46,11 @@ public class ChatsHub : Hub
await base.OnDisconnectedAsync(exception); await base.OnDisconnectedAsync(exception);
} }
public async Task Remove(Guid recipientId, Guid messageId)
{
}
public async Task Edit(string newMessage, Guid messageId) public async Task Edit(string newMessage, Guid messageId)
{ {
@@ -53,7 +58,6 @@ public class ChatsHub : Hub
public async Task Send(MessageRequest request) public async Task Send(MessageRequest request)
{ {
// Валидация входных данных
if (string.IsNullOrWhiteSpace(request.EncryptedContent)) if (string.IsNullOrWhiteSpace(request.EncryptedContent))
{ {
_logger.LogWarning("Empty message received from user {UserId}", GetUserId()); _logger.LogWarning("Empty message received from user {UserId}", GetUserId());
@@ -62,38 +66,43 @@ public class ChatsHub : Hub
var senderId = GetUserId(); var senderId = GetUserId();
// Проверка существования получателя и установленной дружбы
try
{
await _usersRepository.FindByIdAsync(request.RecipientId);
await _verifyFriendship.VerifyAsync(senderId, request.RecipientId);
}
catch (NotFoundByKeyException<User> ex)
{
_logger.LogWarning("Recipient user {ToUserId} not found", request.RecipientId);
throw;
}
catch (ArgumentException ex)
{
_logger.LogWarning("Invalid recipient userId received from user {UserId}", GetUserId());
throw;
}
try try
{ {
_logger.LogInformation("Message sent from {SenderId} to {RecipientId} at {UtcNow}", senderId, request.RecipientId, DateTime.UtcNow); _logger.LogInformation("Message sent from {SenderId} to {RecipientId} at {UtcNow}", senderId, request.RecipientId, DateTime.UtcNow);
// Отправка сообщения отправителю и получателю var message = new SendMessage(
//await Clients.Group(request.RecipientId.ToString()).SendAsync("Receive", message, senderId); EncryptContent: request.EncryptedContent,
// Clients.Group(senderId.ToString()).SendAsync("Receive", message, senderId); ReplyToMessageId: request.ReplyToMessageId,
FromUserId: senderId,
RecipientId: request.RecipientId,
SendAt: DateTime.UtcNow,
Media: request.MediaAttachments?.Select(f => new SendMedia(
f.MediaId, f.EncryptedKey, f.Type, f.MimeType)) ?? Array.Empty<SendMedia>());
if (request.RecipientType == RecipientType.User)
{
await SendUser(message);
}
// TODO: Send to Group
} }
catch (Exception ex) catch (Exception ex)
{ {
//_logger.LogError(ex, "Error sending message from {SenderId} to {RecipientId}", senderId, toUserId); _logger.LogError(ex, "Error sending message from {SenderId} to {RecipientId}", senderId, request.RecipientId);
throw; throw;
} }
} }
private async Task SendUser(SendMessage sendMessage)
{
Result result = await _chatService.SendMessageAsync(sendMessage);
if(result.IsSuccess == false)
throw result.Exception;
// Sending a message to the sender and recipient
await Clients.Group(sendMessage.RecipientId.ToString()).SendAsync("Receive", sendMessage);
await Clients.Group(sendMessage.FromUserId.ToString()).SendAsync("Receive", sendMessage);
}
private Guid GetUserId() private Guid GetUserId()
{ {
var userIdClaim = Context.User?.FindFirst("userID")?.Value; var userIdClaim = Context.User?.FindFirst("userID")?.Value;
@@ -0,0 +1,8 @@
namespace Govor.Application.Interfaces.Messages;
public interface IChatService : IMessageSendingService, IMessageManagementService
{
}
@@ -0,0 +1,9 @@
using Govor.Application.Interfaces.Messages.Parameters;
namespace Govor.Application.Interfaces.Messages;
public interface IMessageManagementService
{
Task<Result> EditMessageAsync(Guid editorId, Guid messageId, string newContent);
Task<Result> DeleteMessageAsync(Guid editorId, Guid messageId);
}
@@ -0,0 +1,6 @@
namespace Govor.Application.Interfaces.Messages;
public interface IMessageReactionService
{
}
@@ -0,0 +1,8 @@
using Govor.Application.Interfaces.Messages.Parameters;
namespace Govor.Application.Interfaces.Messages;
public interface IMessageSendingService
{
Task<Result> SendMessageAsync(SendMessage newMessage);
}
@@ -0,0 +1,3 @@
namespace Govor.Application.Interfaces.Messages.Parameters;
public record Result(bool IsSuccess, Exception Exception);
@@ -0,0 +1,8 @@
using Govor.Core.Models;
namespace Govor.Application.Interfaces.Messages.Parameters;
public record SendMedia(Guid Id,
string EncryptedKey,
MediaType Type,
string MimeType);
@@ -0,0 +1,9 @@
namespace Govor.Application.Interfaces.Messages.Parameters;
public record SendMessage(
string EncryptContent,
Guid? ReplyToMessageId,
Guid RecipientId,
Guid FromUserId,
DateTime SendAt,
IEnumerable<SendMedia> Media);
+1 -6
View File
@@ -26,12 +26,8 @@ public class FriendsService : IFriendsService
{ {
all = await _usersRepository.SearchPotentialFriendsAsync(currentId, query); all = await _usersRepository.SearchPotentialFriendsAsync(currentId, query);
var friends = await _friendshipsRepository.FindByUserIdAsync(currentId);
friends = friends.Where(f => f.Status == FriendshipStatus.Accepted).ToList();
return all return all
.Where(u => u.Id != currentId && !friends.Select(f => f.RequesterId).Contains(u.Id)) .Where(u => u.Id != currentId)
.ToList(); .ToList();
} }
catch (NotFoundByKeyException<(string, Guid)> ex) catch (NotFoundByKeyException<(string, Guid)> ex)
@@ -94,7 +90,6 @@ public class FriendsService : IFriendsService
var friendships = await _friendshipsRepository.FindByUserIdAsync(userId); var friendships = await _friendshipsRepository.FindByUserIdAsync(userId);
return friendships return friendships
.Where(f => f.Status == FriendshipStatus.Accepted)
.Select(f => f.RequesterId == userId ? f.Addressee : f.Requester) .Select(f => f.RequesterId == userId ? f.Addressee : f.Requester)
.ToList(); .ToList();
} }
@@ -0,0 +1,68 @@
using Govor.Application.Interfaces;
using Govor.Application.Interfaces.Messages;
using Govor.Application.Interfaces.Messages.Parameters;
using Govor.Core.Models;
using Govor.Core.Repositories.Messages;
using Govor.Data;
using Microsoft.EntityFrameworkCore;
namespace Govor.Application.Services;
public class PrivateChatService : IChatService
{
private readonly IVerifyFriendship _verifyFriendship;
private readonly IMessagesRepository _messages;
public PrivateChatService(IMessagesRepository messages, IVerifyFriendship verifyFriendship)
{
_messages = messages;
_verifyFriendship = verifyFriendship;
}
public async Task<Result> SendMessageAsync(SendMessage newMessage)
{
try
{
await _verifyFriendship.VerifyAsync(newMessage.FromUserId, newMessage.RecipientId);
var messageId = Guid.NewGuid();
var message = new Message()
{
Id = messageId,
EncryptedContent = newMessage.EncryptContent,
IsEdited = false,
SenderId = newMessage.FromUserId,
RecipientId = newMessage.RecipientId,
RecipientType = RecipientType.User,
ReplyToMessageId = newMessage.ReplyToMessageId,
MediaAttachments = newMessage.Media.Select(m => new MediaAttachments()
{
Id = m.Id,
MessageId = messageId,
Type = m.Type,
MimeType = m.MimeType,
EncryptedKey = m.EncryptedKey,
}).ToList()
};
await _messages.AddAsync(message);
return new Result(true, null);
}
catch (Exception ex)
{
return new Result(false, ex);
}
}
public Task<Result> EditMessageAsync(Guid editorId, Guid messageId, string newContent)
{
throw new NotImplementedException();
}
public Task<Result> DeleteMessageAsync(Guid editorId, Guid messageId)
{
throw new NotImplementedException();
}
}
@@ -1,8 +1,11 @@
using Govor.Core.Models;
namespace Govor.Contracts.Requests.SignalR; namespace Govor.Contracts.Requests.SignalR;
public record MessageRequest public record MessageRequest
{ {
public Guid RecipientId { get; init; } public Guid RecipientId { get; init; }
public RecipientType RecipientType { get; set; }
public string EncryptedContent { get; init; } = string.Empty; public string EncryptedContent { get; init; } = string.Empty;
public Guid? ReplyToMessageId { get; set; } public Guid? ReplyToMessageId { get; set; }
public List<MediaReference> MediaAttachments { get; set; } = new(); public List<MediaReference> MediaAttachments { get; set; } = new();
@@ -46,7 +46,8 @@ public class FriendshipsRepository : IFriendshipsRepository
.Include(x => x.Requester) .Include(x => x.Requester)
.Include(x => x.Addressee) .Include(x => x.Addressee)
.AsSplitQuery() .AsSplitQuery()
.Where(x => x.RequesterId == userId) .Where(f =>
(f.RequesterId == userId || f.AddresseeId == userId))
.ToListOrThrowIfEmpty(new NotFoundByKeyException<Guid>(userId, "Friendship with given user id was not found")); .ToListOrThrowIfEmpty(new NotFoundByKeyException<Guid>(userId, "Friendship with given user id was not found"));
} }
@@ -113,6 +114,7 @@ public class FriendshipsRepository : IFriendshipsRepository
public bool Exist(Guid requesterId, Guid addresseeId) public bool Exist(Guid requesterId, Guid addresseeId)
{ {
return _context.Friendships.AsNoTracking().Any(x => (x.RequesterId == requesterId && x.AddresseeId == addresseeId)); return _context.Friendships.AsNoTracking().Any(x => (x.RequesterId == requesterId && x.AddresseeId == addresseeId) ||
x.RequesterId == addresseeId && x.AddresseeId == requesterId);
} }
} }
+11 -1
View File
@@ -22,6 +22,10 @@ public class UsersRepository : IUsersRepository
{ {
return await _context.Users return await _context.Users
.AsNoTracking() .AsNoTracking()
.Include(u => u.Invite)
.Include(u => u.ReceivedFriendRequests)
.Include(u => u.SentFriendRequests)
.AsSplitQuery()
.ToListOrThrowIfEmpty(new NotFoundException("Users in Database not exists")); .ToListOrThrowIfEmpty(new NotFoundException("Users in Database not exists"));
} }
@@ -48,6 +52,10 @@ public class UsersRepository : IUsersRepository
return await _context.Users return await _context.Users
.AsNoTracking() .AsNoTracking()
.Where(x => ids.Contains(x.Id)) .Where(x => ids.Contains(x.Id))
.Include(u => u.Invite)
.Include(u => u.ReceivedFriendRequests)
.Include(u => u.SentFriendRequests)
.AsSplitQuery()
.ToListOrThrowIfEmpty(new NotFoundByKeyException<IEnumerable<Guid>>(ids,"Users with given ids not found")); .ToListOrThrowIfEmpty(new NotFoundByKeyException<IEnumerable<Guid>>(ids,"Users with given ids not found"));
} }
@@ -70,6 +78,9 @@ public class UsersRepository : IUsersRepository
{ {
return await _context.Users return await _context.Users
.AsNoTracking() .AsNoTracking()
.Include(u => u.Invite)
.Include(u => u.ReceivedFriendRequests)
.Include(u => u.SentFriendRequests)
.AsSplitQuery() .AsSplitQuery()
.Where(u => u.Id != currentUserId && .Where(u => u.Id != currentUserId &&
u.Username.ToLower().Contains(query.ToLower()) && u.Username.ToLower().Contains(query.ToLower()) &&
@@ -211,5 +222,4 @@ public class UsersRepository : IUsersRepository
{ {
return _context.Users.AnyAsync(u => u.Username == username); return _context.Users.AnyAsync(u => u.Username == username);
} }
} }