Remove HasAttachments filter; enhance FetchEmails response

The `MailSearchFilter` class was updated to remove the `HasAttachments` property, simplifying the filtering logic. Corresponding client-side filtering logic in `LimilabsImapEmailService` was also removed. The IMAP search query now defaults to `Expression.All` when no criteria are provided.

The `FetchEmails` method in `EmailController` was enhanced with a new optional `firstHtmlBodyOnly` parameter. This allows returning only the HTML body of the first email or a `404 Not Found` response if no emails match the criteria. These changes improve flexibility and simplify the codebase.
This commit is contained in:
2026-08-10 14:22:45 +02:00
parent 74ec00ddd3
commit ee279c407b
3 changed files with 10 additions and 12 deletions

View File

@@ -19,12 +19,6 @@ public record MailSearchFilter
/// </summary> /// </summary>
public bool UnseenOnly { get; init; } = false; public bool UnseenOnly { get; init; } = false;
/// <summary>
/// When <see langword="true"/>, only messages that carry at least one attachment are returned.
/// Filtered client-side after fetching the message envelope; does not affect the IMAP SEARCH query.
/// </summary>
public bool HasAttachments { get; init; } = false;
/// <summary> /// <summary>
/// When <see langword="true"/>, attachment data is included in the results; otherwise attachments are omitted. /// When <see langword="true"/>, attachment data is included in the results; otherwise attachments are omitted.
/// Defaults to <see langword="false"/>. /// Defaults to <see langword="false"/>.

View File

@@ -78,7 +78,8 @@ public class LimilabsImapEmailService(
} }
// Get UIDs to fetch // Get UIDs to fetch
List<long> uids = [.. await imap.SearchAsync(Expression.And([.. criterions]), cancellationToken)]; var searchExpression = criterions.Count > 0 ? Expression.And([.. criterions]) : Expression.All();
List<long> uids = [.. await imap.SearchAsync(searchExpression, cancellationToken)];
// Apply requested sort order // Apply requested sort order
if (filter.SortOrder == MailSortOrder.NewestFirst) if (filter.SortOrder == MailSortOrder.NewestFirst)
@@ -99,10 +100,6 @@ public class LimilabsImapEmailService(
var mail = new MailBuilder().CreateFromEml(eml); var mail = new MailBuilder().CreateFromEml(eml);
var flags = await imap.GetFlagsByUIDAsync(uid, cancellationToken); var flags = await imap.GetFlagsByUIDAsync(uid, cancellationToken);
// Client-side attachment filter — IMAP has no native criterion for this
if (filter.HasAttachments && mail.Attachments.Count == 0 && mail.Visuals.Count == 0)
continue;
results.Add(MapToContext(uid, mail, flags, filter.WithAttachments)); results.Add(MapToContext(uid, mail, flags, filter.WithAttachments));
} }
catch (Exception ex) catch (Exception ex)

View File

@@ -78,9 +78,16 @@ public class EmailController(IMediator mediator) : ControllerBase
[ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status400BadRequest)] [ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status404NotFound)] [ProducesResponseType(StatusCodes.Status404NotFound)]
public async Task<IActionResult> FetchEmails([FromQuery] FetchEmailsQuery query, CancellationToken cancellationToken = default) public async Task<IActionResult> FetchEmails([FromQuery] FetchEmailsQuery query, [FromQuery] bool firstHtmlBodyOnly = false, CancellationToken cancellationToken = default)
{ {
var emails = await mediator.Send(query, cancellationToken); 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)
return Ok(htmlBody);
return Ok(emails); return Ok(emails);
} }