Add private chat support and group model enhancements

Introduces repositories, validators, and integration tests for private chats. Refactors group-related models to support invitations and memberships, updates group repository interfaces and implementations, and enhances message service logic to handle private chat creation and retrieval. Also registers new services and validators in DI, and updates related tests.
This commit is contained in:
Artemy
2025-07-07 13:55:29 +07:00
parent 66819a015a
commit 2cb6a93060
21 changed files with 454 additions and 54 deletions
+1
View File
@@ -21,6 +21,7 @@ public class GovorDbContext(DbContextOptions<GovorDbContext> options) : DbContex
public virtual DbSet<MediaFile> MediaFiles { get; set; }
public virtual DbSet<ChatGroup> ChatGroups { get; set; }
public virtual DbSet<GroupInvitation> GroupInvitations { get; set; }
public virtual DbSet<GroupMembership> GroupMemberships { get; set; }
public virtual DbSet<GroupAdmins> GroupAdmins { get; set; }
+128 -26
View File
@@ -1,62 +1,164 @@
using System.Text.RegularExpressions;
using Govor.Core.Infrastructure.Extensions;
using Govor.Core.Infrastructure.Validators;
using Govor.Core.Models;
using Govor.Core.Repositories.Groups;
using Govor.Data.Repositories.Exceptions;
using Microsoft.EntityFrameworkCore;
namespace Govor.Data.Repositories;
public class GroupRepository : IGroupsRepository
{
public Task<List<Group>> GetAllAsync()
private GovorDbContext _context;
private IObjectValidator<ChatGroup> _validator;
public GroupRepository(GovorDbContext context, IObjectValidator<ChatGroup> validator)
{
throw new NotImplementedException();
}
public Task<Group> GetByIdAsync(Guid id)
{
throw new NotImplementedException();
}
public Task<List<Group>> FindByNameAsync(string name)
{
throw new NotImplementedException();
_context = context;
_validator = validator;
}
public Task<List<Group>> GetByAdminIdAsync(Guid adminId)
public async Task<List<ChatGroup>> GetAllAsync()
{
throw new NotImplementedException();
return await _context.ChatGroups
.AsNoTracking()
.Include(g => g.Admins)
.Include(g => g.Members)
.Include(g => g.InviteCodes)
.AsSplitQuery()
.ToListOrThrowIfEmpty(new NotFoundException("Database is empty"));
}
public Task<List<Group>> GetByUserIdAsync(Guid adminId)
public async Task<ChatGroup> GetByIdAsync(Guid id)
{
throw new NotImplementedException();
return await _context.ChatGroups
.AsNoTracking()
.Include(g => g.Admins)
.Include(g => g.Members)
.Include(g => g.InviteCodes)
.AsSplitQuery()
.FirstOrDefaultAsync(g => g.Id == id)
?? throw new NotFoundByKeyException<Guid>(id, "Group with given id doesn't exist");
}
public Task Add(Group group)
public async Task<List<ChatGroup>> SearchByNameAsync(string name)
{
throw new NotImplementedException();
return await _context.ChatGroups
.AsNoTracking()
.Include(g => g.Admins)
.Include(g => g.Members)
.Include(g => g.InviteCodes)
.AsSplitQuery()
.Where(g => g.Name.ToLower().Contains(name.ToLower()))
.Take(20)
.ToListOrThrowIfEmpty(new NotFoundByKeyException<string>(name, "Group with name doesn't exist"));
}
public async Task<List<ChatGroup>> GetByAdminIdAsync(Guid userId)
{
return await _context.ChatGroups
.AsNoTracking()
.Include(g => g.Admins)
.Include(g => g.Members)
.Include(g => g.InviteCodes)
.AsSplitQuery()
.Where(g => g.Admins.Any(a => a.UserId == userId))
.ToListOrThrowIfEmpty(new NotFoundByKeyException<Guid>(userId, "Admin with given id doesn't exist"));
}
public Task Update(Group group)
public async Task<List<ChatGroup>> GetByUserIdAsync(Guid userId)
{
throw new NotImplementedException();
return await _context.ChatGroups
.AsNoTracking()
.Include(g => g.Admins)
.Include(g => g.Members)
.Include(g => g.InviteCodes)
.AsSplitQuery()
.Where(g => g.Members.Any(a => a.UserId == userId))
.ToListOrThrowIfEmpty(new NotFoundByKeyException<Guid>(userId, "Member with given id doesn't exist"));
}
public Task Remove(Guid groupId)
public async Task AddAsync(ChatGroup group)
{
throw new NotImplementedException();
try
{
_validator.Validate(group);
_context.ChatGroups.Add(group);
await _context.SaveChangesAsync();
}
catch (InvalidObjectException<ChatGroup> ex)
{
throw new AdditionException("Group with given data invalid", ex);
}
catch (Exception ex)
{
throw new AdditionException("Cannot add group", ex);
}
}
public async Task UpdateAsync(ChatGroup group)
{
try
{
_validator.Validate(group);
var rowsAffected = await _context.ChatGroups
.Where(u => u.Id == group.Id)
.ExecuteUpdateAsync(g => g
.SetProperty(g => g.Name, group.Name)
.SetProperty(g => g.Description, group.Description)
.SetProperty(g => g.ImageId, group.ImageId)
.SetProperty(g => g.IsChannel, group.IsChannel)
.SetProperty(g => g.IsPrivate, group.IsPrivate)
.SetProperty(g => g.InviteCodes, group.InviteCodes)
);
if (rowsAffected == 0)
throw new UpdateException($"Not found group by given id {group.Id}");
}
catch (Exception ex)
{
throw new UpdateException($"Error when updating the group {group.Id}", ex);
}
}
public async Task RemoveAsync(Guid groupId)
{
try
{
var result = await GetByIdAsync(groupId);
_context.ChatGroups.Remove(result);
await _context.SaveChangesAsync();
}
catch (InvalidObjectException<User> ex)
{
throw new RemoveException("User with given data invalid", ex);
}
}
public bool Exists(Guid groupId)
{
throw new NotImplementedException();
return _context.ChatGroups.Any(g => g.Id == groupId);
}
public bool Exists(ChatGroup chatGroup)
{
throw new NotImplementedException();
return _context.ChatGroups.Any(g => g.Id == chatGroup.Id &&
g.IsChannel == chatGroup.IsChannel &&
g.Description == chatGroup.Description &&
g.IsPrivate == chatGroup.IsPrivate &&
g.Name == chatGroup.Name &&
g.ImageId == chatGroup.ImageId);
}
public bool IsUserMemberOfGroupAsync(Guid userId, Guid groupId)
public async Task<bool> IsUserMemberOfGroupAsync(Guid userId, Guid groupId)
{
throw new NotImplementedException();
return await _context.ChatGroups
.AsNoTracking()
.Include(g => g.Members)
.AnyAsync(g => g.Members.Any(a => a.UserId == userId && a.GroupId == groupId));
}
}
@@ -0,0 +1,115 @@
using Govor.Core.Infrastructure.Extensions;
using Govor.Core.Infrastructure.Validators;
using Govor.Core.Models;
using Govor.Core.Repositories.PrivateChats;
using Govor.Data.Repositories.Exceptions;
using Microsoft.EntityFrameworkCore;
namespace Govor.Data.Repositories;
public class PrivateChatsRepository : IPrivateChatsRepository
{
private GovorDbContext _context;
private IObjectValidator<PrivateChat> _validator;
public PrivateChatsRepository(GovorDbContext context, IObjectValidator<PrivateChat> validator)
{
_context = context;
_validator = validator;
}
public async Task<List<PrivateChat>> GetAllAsync()
{
return await _context.PrivateChats
.AsNoTracking()
.ToListOrThrowIfEmpty(new NotFoundException("Database is empty"));
}
public async Task<PrivateChat> GetByIdAsync(Guid id)
{
return await _context.PrivateChats
.AsNoTracking()
.FirstOrDefaultAsync(p => p.Id == id)
?? throw new NotFoundByKeyException<Guid>(id, "Private Chat with given Id does not exist");
}
public async Task<PrivateChat> GetByMembersAsync(Guid memberAId, Guid memberBId)
{
return await _context.PrivateChats
.AsNoTracking()
.FirstOrDefaultAsync(f => (f.UserAId == memberAId && f.UserBId == memberBId) || (f.UserAId == memberBId && f.UserBId == memberAId))
?? throw new NotFoundByKeyException<(Guid, Guid)>((memberAId, memberBId), "Private Chat with given members Id's does not exist");
}
public async Task AddAsync(PrivateChat chat)
{
try
{
_validator.Validate(chat);
_context.PrivateChats.Add(chat);
await _context.SaveChangesAsync();
}
catch (InvalidObjectException<PrivateChat> ex)
{
throw new AdditionException("Private chat with given data invalid", ex);
}
catch (Exception ex)
{
throw new AdditionException("Cannot add private chat", ex);
}
}
public async Task UpdateAsync(PrivateChat chat)
{
try
{
_validator.Validate(chat);
var rowsAffected = await _context.PrivateChats
.Where(m => m.Id == chat.Id)
.ExecuteUpdateAsync(c => c
.SetProperty(c => c.UserAId, chat.UserAId)
.SetProperty(c => c.UserBId, chat.UserBId)
.SetProperty(c => c.Messages, chat.Messages)
);
if (rowsAffected == 0)
throw new UpdateException($"Not found private chat by given id {chat.Id}");
}
catch (Exception ex)
{
throw new UpdateException($"Error when updating the private chat {chat.Id}", ex);
}
}
public async Task RemoveAsync(Guid chatId)
{
try
{
var result = await GetByIdAsync(chatId);
_context.PrivateChats.Remove(result);
await _context.SaveChangesAsync();
}
catch (NotFoundByKeyException<Guid> ex)
{
throw new RemoveException($"Not found private chat by given id {chatId}", ex);
}
catch (Exception ex)
{
throw new RemoveException("Error when removing the private chat", ex);
}
}
public bool Exist(Guid chatId)
{
return _context.PrivateChats.Any(c => c.Id == chatId);
}
public bool Exist(Guid userAId, Guid userBId)
{
return _context.PrivateChats.Any(f => (f.UserAId == userAId && f.UserBId == userBId)
|| (f.UserAId == userBId && f.UserBId == userAId));
}
}