Refactor email fetching logic in EmailController
Consolidated email fetching endpoints by removing `FetchEmailUids` and `FetchEmailByUid` endpoints and integrating their functionality into the `FetchEmails` method. Introduced an `OnlyFilter` enum to allow filtering responses for HTML body or UIDs. Removed related methods (`FetchEmailUidsAsync`, `FetchEmailByUidAsync`) from `IImapEmailService` and deleted associated query classes and handlers.
This commit is contained in:
@@ -22,27 +22,6 @@ public interface IImapEmailService
|
||||
MailSearchFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Fetches only the UIDs of messages matching the specified filter.
|
||||
/// </summary>
|
||||
Task<IEnumerable<long>> FetchEmailUidsAsync(
|
||||
EmailAccountDto account,
|
||||
MailSearchFilter filter,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Fetches a single email by its UID.
|
||||
/// </summary>
|
||||
/// When <see langword="true"/> (default), the message is marked as <c>\Seen</c> on the server.
|
||||
/// Set to <see langword="false"/> for a non-destructive read.
|
||||
/// </param>
|
||||
Task<ReceivedEmailContext?> FetchEmailByUidAsync(
|
||||
EmailAccountDto account,
|
||||
long uid,
|
||||
string folder = "INBOX",
|
||||
bool withAttachments = false,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Marks a message as seen (read) on the server.
|
||||
/// </summary>
|
||||
|
||||
@@ -1,62 +0,0 @@
|
||||
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;
|
||||
|
||||
/// <summary>
|
||||
/// Query to fetch a single email by its UID from an IMAP mailbox.
|
||||
/// </summary>
|
||||
public record FetchEmailByUidQuery : IRequest<ReceivedEmailContext?>
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies the email account to use.
|
||||
/// </summary>
|
||||
public required GetSenderQuery Account { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// UID of the message to fetch.
|
||||
/// </summary>
|
||||
internal long? Uid { get; private set; } = null;
|
||||
|
||||
public FetchEmailByUidQuery WithUid(long uid)
|
||||
{
|
||||
Uid = uid;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mailbox folder the message resides in (default: "INBOX").
|
||||
/// </summary>
|
||||
public string Folder { get; init; } = "INBOX";
|
||||
|
||||
/// <summary>
|
||||
/// When <see langword="true"/>, attachment data is included in the result.
|
||||
/// </summary>
|
||||
public bool WithAttachments { get; init; } = false;
|
||||
}
|
||||
|
||||
public class FetchEmailByUidQueryHandler(
|
||||
ISender Sender,
|
||||
IImapEmailService ImapService) : IRequestHandler<FetchEmailByUidQuery, ReceivedEmailContext?>
|
||||
{
|
||||
public async Task<ReceivedEmailContext?> 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);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
using DigitalData.MessagingService.Application.Common.Interfaces;
|
||||
using DigitalData.MessagingService.Application.Common.Models.MailSearch;
|
||||
using DigitalData.MessagingService.Application.EmailAccount.Queries;
|
||||
using DigitalData.MessagingService.Domain.Exceptions;
|
||||
using MediatR;
|
||||
|
||||
namespace DigitalData.MessagingService.Application.EmailReceiving.Queries;
|
||||
|
||||
/// <summary>
|
||||
/// Query to fetch only the UIDs of matching emails from an IMAP mailbox.
|
||||
/// </summary>
|
||||
public record FetchEmailUidsQuery : IRequest<IEnumerable<long>>
|
||||
{
|
||||
/// <summary>
|
||||
/// Identifies the email account to use.
|
||||
/// </summary>
|
||||
public required GetSenderQuery Account { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Mail query used to filter and limit the emails retrieved.
|
||||
/// </summary>
|
||||
public MailSearchFilter Mail { get; init; } = new();
|
||||
}
|
||||
|
||||
public class FetchEmailUidsQueryHandler(ISender Sender, IImapEmailService ImapService) : IRequestHandler<FetchEmailUidsQuery, IEnumerable<long>>
|
||||
{
|
||||
public async Task<IEnumerable<long>> Handle(FetchEmailUidsQuery 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.FetchEmailUidsAsync(
|
||||
account,
|
||||
request.Mail,
|
||||
cancellationToken);
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,21 @@ namespace DigitalData.MessagingService.API.Controllers;
|
||||
[Route("api/[controller]")]
|
||||
public class EmailController(IMediator mediator) : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public enum OnlyFilter
|
||||
{
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
HtmlBody,
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
Uid,
|
||||
}
|
||||
|
||||
#region Send
|
||||
/// <summary>
|
||||
/// Send an email, optionally with file attachments.
|
||||
@@ -72,72 +87,33 @@ public class EmailController(IMediator mediator) : ControllerBase
|
||||
/// Fetch emails from an IMAP mailbox.
|
||||
/// </summary>
|
||||
/// <param name="query">Query parameters for filtering and fetching emails from the IMAP mailbox.</param>
|
||||
/// <param name="firstHtmlBodyOnly"></param>
|
||||
/// <param name="only"></param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>HTTP 200 with list of received emails.</returns>
|
||||
[HttpGet]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> FetchEmails([FromQuery] FetchEmailsQuery query, [FromQuery] bool firstHtmlBodyOnly = false, CancellationToken cancellationToken = default)
|
||||
public async Task<IActionResult> FetchEmails([FromQuery] FetchEmailsQuery query, [FromQuery] OnlyFilter? only = null, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var emails = await mediator.Send(query, cancellationToken);
|
||||
|
||||
if(!emails.Any())
|
||||
return NotFound("No emails found matching the specified criteria.");
|
||||
|
||||
if (firstHtmlBodyOnly && emails.FirstOrDefault()?.HtmlBody is string htmlBody)
|
||||
if (only == OnlyFilter.HtmlBody)
|
||||
{
|
||||
if (emails.FirstOrDefault()?.HtmlBody is string htmlBody)
|
||||
return Content(htmlBody, "text/html");
|
||||
|
||||
else
|
||||
return NotFound();
|
||||
}
|
||||
else if (only == OnlyFilter.Uid)
|
||||
return Ok(emails.Select(e => e.Uid).ToList());
|
||||
else
|
||||
return Ok(emails);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetch only the UIDs of emails matching the given filter from an IMAP mailbox.
|
||||
/// </summary>
|
||||
/// <param name="query">Query parameters for filtering emails.</param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>HTTP 200 with the list of matching UIDs, or HTTP 404 if none found.</returns>
|
||||
[HttpGet("uid")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> FetchEmailUids([FromQuery] FetchEmailUidsQuery query, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var uids = await mediator.Send(query, cancellationToken);
|
||||
|
||||
if (!uids.Any())
|
||||
return NotFound("No emails found matching the specified criteria.");
|
||||
|
||||
return Ok(uids);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fetch a single email by its UID from an IMAP mailbox.
|
||||
/// </summary>
|
||||
/// <param name="uid">The unique identifier (UID) of the message.</param>
|
||||
/// <param name="query">Query parameters including account, folder and attachment flag.</param>
|
||||
/// <param name="htmlBodyOnly"></param>
|
||||
/// <param name="cancellationToken">Cancellation token.</param>
|
||||
/// <returns>HTTP 200 with the matched email, or HTTP 404 if not found.</returns>
|
||||
[HttpGet("{uid:long}")]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||
public async Task<IActionResult> FetchEmailByUid([FromRoute] long uid, [FromQuery] FetchEmailByUidQuery query, [FromQuery] bool htmlBodyOnly = false, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var queryWithUid = query.WithUid(uid);
|
||||
var email = await mediator.Send(queryWithUid, cancellationToken);
|
||||
|
||||
if (email is null)
|
||||
return NotFound($"No email found with UID {uid}.");
|
||||
|
||||
if (htmlBodyOnly)
|
||||
return Content(email.HtmlBody, "text/html");
|
||||
|
||||
return Ok(email);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Mark a single IMAP message as seen (read).
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user