diff --git a/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/FetchEmailByUidQuery.cs b/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/FetchEmailByUidQuery.cs new file mode 100644 index 0000000..9597666 --- /dev/null +++ b/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/FetchEmailByUidQuery.cs @@ -0,0 +1,62 @@ +using DigitalData.MessagingService.Abstraction; +using DigitalData.MessagingService.Application.Common.Interfaces; +using DigitalData.MessagingService.Application.EmailAccount.Queries; +using DigitalData.MessagingService.Domain.Exceptions; +using MediatR; + +namespace DigitalData.MessagingService.Application.EmailReceiving.Queries; + +/// +/// Query to fetch a single email by its UID from an IMAP mailbox. +/// +public record FetchEmailByUidQuery : IRequest +{ + /// + /// Identifies the email account to use. + /// + public required GetSenderQuery Account { get; init; } + + /// + /// UID of the message to fetch. + /// + internal long? Uid { get; private set; } = null; + + public FetchEmailByUidQuery WithUid(long uid) + { + Uid = uid; + return this; + } + + /// + /// Mailbox folder the message resides in (default: "INBOX"). + /// + public string Folder { get; init; } = "INBOX"; + + /// + /// When , attachment data is included in the result. + /// + public bool WithAttachments { get; init; } = false; +} + +public class FetchEmailByUidQueryHandler( + ISender Sender, + IImapEmailService ImapService) : IRequestHandler +{ + public async Task Handle(FetchEmailByUidQuery request, CancellationToken cancellationToken) + { + var account = await Sender.Send(request.Account, cancellationToken) + ?? throw new NotFoundException( + $"No email account found for the given criteria (Id: {request.Account.Id}, Username: {request.Account.Username})."); + + if (string.IsNullOrWhiteSpace(account.ImapServer)) + throw new InvalidOperationException( + $"IMAP is not configured for account '{account.Username}' (Id: {account.Id}). Set ImapServer in EmailAccounts configuration."); + + return await ImapService.FetchEmailByUidAsync( + account, + (long)request.Uid!, + request.Folder, + request.WithAttachments, + cancellationToken); + } +} diff --git a/src/core/DigitalData.MessagingService.Application/EmailReceiving/Validators/FetchEmailByUidQueryValidator.cs b/src/core/DigitalData.MessagingService.Application/EmailReceiving/Validators/FetchEmailByUidQueryValidator.cs new file mode 100644 index 0000000..c9ecfe8 --- /dev/null +++ b/src/core/DigitalData.MessagingService.Application/EmailReceiving/Validators/FetchEmailByUidQueryValidator.cs @@ -0,0 +1,27 @@ +using DigitalData.MessagingService.Application.EmailReceiving.Queries; +using FluentValidation; + +namespace DigitalData.MessagingService.Application.EmailReceiving.Validators; + +/// +/// Validates a before it is handled by . +/// +public class FetchEmailByUidQueryValidator : AbstractValidator +{ + public FetchEmailByUidQueryValidator() + { + RuleFor(x => x.Account) + .NotNull() + .WithMessage("Account query must not be null."); + + RuleFor(x => x.Uid) + .NotNull() + .WithMessage("UID must be provided. Use WithUid() to set the UID before dispatching the query.") + .GreaterThan(0) + .WithMessage("UID must be greater than 0."); + + RuleFor(x => x.Folder) + .NotEmpty() + .WithMessage("Folder must not be empty."); + } +}