Refactor friend request services and add SignalR error handling

Split IFriendRequestService into command and query interfaces, refactor related services and tests, and update dependency injection. Add HubResult response model and a SignalR HubExceptionFilter for consistent error handling. Move and update FriendsHub to use new command service and result pattern. Update username validation to allow digits after Cyrillic letters. Add new controllers and configuration for SignalR. Remove obsolete IFriendRequestService and related code.
This commit is contained in:
Artemy
2025-07-10 15:31:57 +07:00
parent b1f3aa0266
commit 437bedb117
20 changed files with 571 additions and 205 deletions
@@ -8,7 +8,7 @@ namespace Govor.Application.Infrastructure.Validators;
public class UsernameValidator : IUsernameValidator
{
private readonly Regex _usernameRegex = new(@"^[А-Яа-яЁё]+$", RegexOptions.Compiled);
private readonly Regex _usernameRegex = new(@"^[А-Яа-яЁё]+[А-Яа-яЁё0-9]*$", RegexOptions.Compiled);
public void Validate(string username)
{
@@ -0,0 +1,8 @@
namespace Govor.Application.Interfaces.Friends;
public interface IFriendRequestCommandService
{
Task SendAsync(Guid fromUserId, Guid toUserId);
Task AcceptAsync(Guid requestId, Guid currentUserId);
Task RejectAsync(Guid requestId, Guid currentUserId);
}
@@ -0,0 +1,10 @@
using Govor.Core.Models;
namespace Govor.Application.Interfaces.Friends;
public interface IFriendRequestQueryService
{
Task<List<Friendship>> GetIncomingAsync(Guid userId);
Task<List<Friendship>> GetResponsesAsync(Guid userId);
}
@@ -1,12 +0,0 @@
using Govor.Core.Models;
namespace Govor.Application.Interfaces.Friends;
public interface IFriendRequestService
{
Task SendFriendRequestAsync(Guid fromUserId, Guid toUserId);
Task AcceptFriendRequestAsync(Guid requestId, Guid currentUserId);
Task RejectFriendRequestAsync(Guid requestId, Guid currentUserId);
Task<List<Friendship>> GetIncomingRequestsAsync(Guid userId);
Task<List<Friendship>> GetResponsesAsync(Guid userId);
}
@@ -6,16 +6,16 @@ using Govor.Data.Repositories.Exceptions;
namespace Govor.Application.Services.Friends;
public class FriendRequestService : IFriendRequestService
public class FriendRequestCommandService : IFriendRequestCommandService
{
private readonly IFriendshipsRepository _friendshipsRepository;
public FriendRequestService(IFriendshipsRepository friendshipsRepository)
public FriendRequestCommandService(IFriendshipsRepository friendshipsRepository)
{
_friendshipsRepository = friendshipsRepository;
}
public async Task SendFriendRequestAsync(Guid fromUserId, Guid toUserId)
public async Task SendAsync(Guid fromUserId, Guid toUserId)
{
if (fromUserId == toUserId)
throw new InvalidOperationException("Cannot send a request to self user");
@@ -32,7 +32,7 @@ public class FriendRequestService : IFriendRequestService
});
}
public async Task AcceptFriendRequestAsync(Guid requestId, Guid currentUserId)
public async Task AcceptAsync(Guid requestId, Guid currentUserId)
{
try
{
@@ -53,7 +53,7 @@ public class FriendRequestService : IFriendRequestService
}
}
public async Task RejectFriendRequestAsync(Guid requestId, Guid currentUserId)
public async Task RejectAsync(Guid requestId, Guid currentUserId)
{
try
{
@@ -73,32 +73,4 @@ public class FriendRequestService : IFriendRequestService
throw new InvalidOperationException("Friendship not found! You cant reject request!", ex);
}
}
public async Task<List<Friendship>> GetIncomingRequestsAsync(Guid userId)
{
try
{
var friendships = await _friendshipsRepository.FindByUserIdAsync(userId);
return friendships.Where(f => f.AddresseeId == userId && f.Status == FriendshipStatus.Pending).ToList()
?? new List<Friendship>();
}
catch (NotFoundByKeyException<Guid> ex)
{
throw new InvalidOperationException("User not exist", ex);
}
}
public async Task<List<Friendship>> GetResponsesAsync(Guid userId)
{
try
{
var friendships = await _friendshipsRepository.FindByUserIdAsync(userId);
return friendships.Where(f => f.RequesterId == userId && f.Status != FriendshipStatus.Accepted).ToList()
?? new List<Friendship>();
}
catch (NotFoundByKeyException<Guid> ex)
{
throw new InvalidOperationException("User not exist", ex);
}
}
}
@@ -0,0 +1,44 @@
using Govor.Application.Interfaces.Friends;
using Govor.Core.Models;
using Govor.Core.Repositories.Friendships;
using Govor.Data.Repositories.Exceptions;
namespace Govor.Application.Services.Friends;
public class FriendRequestQueryService : IFriendRequestQueryService
{
private readonly IFriendshipsRepository _friendshipsRepository;
public FriendRequestQueryService(IFriendshipsRepository friendshipsRepository)
{
_friendshipsRepository = friendshipsRepository;
}
public async Task<List<Friendship>> GetIncomingAsync(Guid userId)
{
try
{
var friendships = await _friendshipsRepository.FindByUserIdAsync(userId);
return friendships.Where(f => f.AddresseeId == userId && f.Status == FriendshipStatus.Pending).ToList()
?? new List<Friendship>();
}
catch (NotFoundByKeyException<Guid> ex)
{
throw new InvalidOperationException("User not exist", ex);
}
}
public async Task<List<Friendship>> GetResponsesAsync(Guid userId)
{
try
{
var friendships = await _friendshipsRepository.FindByUserIdAsync(userId);
return friendships.Where(f => f.RequesterId == userId && f.Status != FriendshipStatus.Accepted).ToList()
?? new List<Friendship>();
}
catch (NotFoundByKeyException<Guid> ex)
{
throw new InvalidOperationException("User not exist", ex);
}
}
}