diff --git a/src/core/DigitalData.MessagingService.Application/Common/Interfaces/Repositories/IReceivedEmailRepository.cs b/src/core/DigitalData.MessagingService.Application/Common/Interfaces/Repositories/IReceivedEmailRepository.cs index ed3210b..f58d6bd 100644 --- a/src/core/DigitalData.MessagingService.Application/Common/Interfaces/Repositories/IReceivedEmailRepository.cs +++ b/src/core/DigitalData.MessagingService.Application/Common/Interfaces/Repositories/IReceivedEmailRepository.cs @@ -6,6 +6,6 @@ namespace DigitalData.MessagingService.Application.Common.Interfaces.Repositorie public interface IReceivedEmailRepository : IRepository { - public Task> FindAsync(MailSearchFilter mailSearchFilter, EmailAccount accountQuery, CancellationToken cancellationToken = default); + public Task> FindAsync(MailSearchFilter mailSearchFilter, EmailAccount? accountQuery = null, CancellationToken cancellationToken = default); } #endif \ No newline at end of file diff --git a/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailQuery.cs b/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailQuery.cs index 85a1d36..0e8bf74 100644 --- a/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailQuery.cs +++ b/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailQuery.cs @@ -28,7 +28,7 @@ public record ReadEmailQuery : IRequest> public MailSearchFilter Mail { get; init; } = new(); } -public class ReadEmailQueryHandler(IMapper Mapper, ILogger Logger, IRepository EmailAccountRepo, IMailRepository MailRepo) : IRequestHandler> +public class ReadEmailQueryHandler(IMapper Mapper, ILogger Logger, IRepository EmailAccountRepo, IReceivedEmailRepository MailRepo) : IRequestHandler> { public async Task> Handle(ReadEmailQuery request, CancellationToken cancellationToken) { diff --git a/src/infrastructure/DigitalData.MessagingService.Infrastructure/DependencyInjection.cs b/src/infrastructure/DigitalData.MessagingService.Infrastructure/DependencyInjection.cs index 79fc6c6..d1444cc 100644 --- a/src/infrastructure/DigitalData.MessagingService.Infrastructure/DependencyInjection.cs +++ b/src/infrastructure/DigitalData.MessagingService.Infrastructure/DependencyInjection.cs @@ -66,6 +66,8 @@ public static class DependencyInjection services.AddScoped(typeof(IRepository<>), typeof(Repository<>)); + services.AddScoped(); + // AutoMapper - Register entity self-mappings (T -> T) for generic repository services.AddAutoMapper(config => config.AddMaps(typeof(EntitySelfMappingProfile).Assembly)); diff --git a/src/infrastructure/DigitalData.MessagingService.Infrastructure/Repositories/ReceivedEmailRepository.cs b/src/infrastructure/DigitalData.MessagingService.Infrastructure/Repositories/ReceivedEmailRepository.cs new file mode 100644 index 0000000..7a8cf6c --- /dev/null +++ b/src/infrastructure/DigitalData.MessagingService.Infrastructure/Repositories/ReceivedEmailRepository.cs @@ -0,0 +1,83 @@ +using AutoMapper; +using DigitalData.MessagingService.Application.Common.Dto.MailSearch; +using DigitalData.MessagingService.Application.Common.Interfaces.Repositories; +using DigitalData.MessagingService.Domain.Entities; +using DigitalData.MessagingService.Infrastructure.Persistence; +using Microsoft.EntityFrameworkCore; + +namespace DigitalData.MessagingService.Infrastructure.Repositories; + +public class ReceivedEmailRepository(MessagingServiceDbContext Context, IMapper Mapper) : Repository(Context, Mapper), IReceivedEmailRepository +{ + public async Task> FindAsync(MailSearchFilter mailSearchFilter, EmailAccount? accountQuery = null, CancellationToken cancellationToken = default) + { + IQueryable query = DbSet; + + // ── Account filter ───────────────────────────────────────────────────── + if (accountQuery is not null) + query = query.Where(x => x.AccountId == accountQuery.Id); + + // ── Flag filters ─────────────────────────────────────────────────────── + if (mailSearchFilter.UnseenOnly) + query = query.Where(x => !x.IsSeen); + + // ── Text filters ─────────────────────────────────────────────────────── + if (!string.IsNullOrWhiteSpace(mailSearchFilter.SubjectContains)) + query = query.Where(x => x.Subject.Contains(mailSearchFilter.SubjectContains)); + + if (!string.IsNullOrWhiteSpace(mailSearchFilter.SenderContains)) + query = query.Where(x => x.From.Contains(mailSearchFilter.SenderContains)); + + if (!string.IsNullOrWhiteSpace(mailSearchFilter.BodyContains)) + query = query.Where(x => x.TextBody.Contains(mailSearchFilter.BodyContains) + || x.HtmlBody.Contains(mailSearchFilter.BodyContains)); + + // ── UID filter ───────────────────────────────────────────────────────── + if (mailSearchFilter.Uid is { } uid) + { + if (uid.Absolute.HasValue) + query = query.Where(x => x.Uid == uid.Absolute.Value); + else + { + if (uid.Min.HasValue) + query = query.Where(x => x.Uid >= uid.Min.Value); + if (uid.Max.HasValue) + query = query.Where(x => x.Uid <= uid.Max.Value); + } + } + + // ── Date filter ──────────────────────────────────────────────────────── + if (mailSearchFilter.Date is { } date) + { + if (date.After.HasValue) + query = query.Where(x => x.Date >= date.After.Value); + if (date.Before.HasValue) + query = query.Where(x => x.Date <= date.Before.Value); + } + + // ── Attachments ──────────────────────────────────────────────────────── + if (mailSearchFilter.WithAttachments) + query = query.Include(x => x.Attachments); + + // ── Sort ─────────────────────────────────────────────────────────────── + query = mailSearchFilter.SortOrder == MailSortOrder.OldestFirst + ? query.OrderBy(x => x.Date) + : query.OrderByDescending(x => x.Date); + + // ── Limit ────────────────────────────────────────────────────────────── + if (mailSearchFilter.MaxCount.HasValue) + query = query.Take(mailSearchFilter.MaxCount.Value); + + // ── RecipientContains: To/Cc are IEnumerable (nvarchar(max)) ── + // EF Core cannot translate collection predicates on these columns to SQL. + // Materialization is deferred until after other DB-side filters narrow the set. + var results = await query.ToListAsync(cancellationToken); + + if (!string.IsNullOrWhiteSpace(mailSearchFilter.RecipientContains)) + results = [.. results + .Where(x => x.To.Any(t => t.Contains(mailSearchFilter.RecipientContains, StringComparison.OrdinalIgnoreCase)) + || x.Cc.Any(c => c.Contains(mailSearchFilter.RecipientContains, StringComparison.OrdinalIgnoreCase)))]; + + return results; + } +} \ No newline at end of file