diff --git a/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailQuery.cs b/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailQuery.cs
new file mode 100644
index 0000000..1c5c943
--- /dev/null
+++ b/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailQuery.cs
@@ -0,0 +1,125 @@
+#if NET
+using AutoMapper;
+using DigitalData.MessagingService.Application.Common.Dto;
+using DigitalData.MessagingService.Application.Common.Dto.MailSearch;
+using DigitalData.MessagingService.Application.Common.Interfaces;
+using DigitalData.MessagingService.Application.Common.Interfaces.Repositories;
+using DigitalData.MessagingService.Application.EmailAccounts.Queries;
+using DigitalData.MessagingService.Domain.Entities;
+using DigitalData.MessagingService.Domain.Enums;
+using DigitalData.MessagingService.Domain.Exceptions;
+using MediatR;
+using Microsoft.Extensions.Logging;
+
+namespace DigitalData.MessagingService.Application.EmailReceiving.Queries;
+
+///
+/// Query to fetch emails from an email account.
+/// The protocol and authentication method are determined by .
+///
+public record ReadEmailQuery : IRequest
+{
+ ///
+ /// Identifies the email account to use.
+ ///
+ public required GetEmailAccountQuery Account { get; init; }
+
+ ///
+ /// Mail query used to filter and limit the emails retrieved.
+ ///
+ public MailSearchFilter Mail { get; init; } = new();
+}
+
+public class ReadEmailQueryHandler(
+ IMapper Mapper,
+ ILogger Logger,
+ IRepository EmailAccountRepo,
+ IReceivedEmailRepository MailRepo,
+ IImapEmailService ImapEmailService,
+ IPop3EmailService Pop3EmailService) : IRequestHandler
+{
+ public async Task Handle(ReadEmailQuery request, CancellationToken cancellationToken)
+ {
+ var accounts = await EmailAccountRepo.FindAsync(
+ request.Account.Id is int id ? x => x.Id == id : x => x.Username == request.Account.Username,
+ cancellationToken: cancellationToken);
+
+ if (accounts.Count() > 1)
+ Logger.LogWarning("Multiple email accounts found for the given criteria ({Criteria}). Returning the first one.",
+ request.Account.Id is not null ? $"Id: {request.Account.Id}" : $"Username: {request.Account.Username}");
+
+ var account = accounts.FirstOrDefault()
+ ?? throw new NotFoundException($"No email account found for the given criteria (Id: {request.Account.Id}, Username: {request.Account.Username}).");
+
+ return account.IncomingProtocol switch
+ {
+ IncomingProtocol.Imap => await HandleImapAsync(account, request.Mail, cancellationToken),
+ IncomingProtocol.ImapOAuth2 => await HandleImapOAuth2Async(account, request.Mail, cancellationToken),
+ IncomingProtocol.Pop3 => await HandlePop3Async(account, request.Mail, cancellationToken),
+ IncomingProtocol.Pop3OAuth2 => throw new NotImplementedException(
+ $"POP3 OAuth2 is not yet supported. Account: '{account.Username}' (Id: {account.Id})."),
+ IncomingProtocol.None => throw new BadRequestException(
+ $"Account '{account.Username}' (Id: {account.Id}) has no incoming protocol configured (IncomingProtocol = None)."),
+ _ => throw new BadRequestException(
+ $"Unknown IncomingProtocol value '{account.IncomingProtocol}' for account '{account.Username}' (Id: {account.Id}).")
+ };
+ }
+
+ private async Task HandleImapAsync(EmailAccount account, MailSearchFilter filter, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrWhiteSpace(account.ImapServer))
+ throw new BadRequestException(
+ $"IMAP is not configured for account '{account.Username}' (Id: {account.Id}). Set ImapServer in EmailAccounts configuration.");
+
+ var mails = await MailRepo.FindAsync(filter, account, cancellationToken);
+ var lastSync = ImapEmailService.GetLastImapSyncDate(account.Id, filter.Folder);
+
+ return new ReadEmailQueryResponse
+ {
+ LastSync = lastSync,
+ Emails = Mapper.Map>(mails)
+ };
+ }
+
+ private async Task HandleImapOAuth2Async(EmailAccount account, MailSearchFilter filter, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrWhiteSpace(account.OAuth2ClientId) || string.IsNullOrWhiteSpace(account.OAuth2ClientSecret))
+ throw new BadRequestException(
+ $"OAuth2 credentials (ClientId, ClientSecret) are not configured for account '{account.Username}' (Id: {account.Id}).");
+
+ if (string.IsNullOrWhiteSpace(account.ImapServer))
+ throw new BadRequestException(
+ $"IMAP is not configured for account '{account.Username}' (Id: {account.Id}). Set ImapServer in EmailAccounts configuration.");
+
+ var mails = await MailRepo.FindAsync(filter, account, cancellationToken);
+ var lastSync = ImapEmailService.GetLastImapSyncDate(account.Id, filter.Folder);
+
+ return new ReadEmailQueryResponse
+ {
+ LastSync = lastSync,
+ Emails = Mapper.Map>(mails)
+ };
+ }
+
+ private async Task HandlePop3Async(EmailAccount account, MailSearchFilter filter, CancellationToken cancellationToken)
+ {
+ if (string.IsNullOrWhiteSpace(account.Pop3Server))
+ throw new BadRequestException(
+ $"POP3 is not configured for account '{account.Username}' (Id: {account.Id}). Set Pop3Server in EmailAccounts configuration.");
+
+ // Trigger on-demand POP3 sync before querying local storage
+ await Pop3EmailService.SyncEmailsAsync(account, cancellationToken);
+
+ // POP3 has no folder concept — always query INBOX
+ var pop3Filter = filter with { Folder = "INBOX" };
+ var mails = await MailRepo.FindAsync(pop3Filter, account, cancellationToken);
+ var lastSync = Pop3EmailService.GetLastPop3SyncDate(account.Id);
+
+ return new ReadEmailQueryResponse
+ {
+ LastSync = lastSync,
+ Emails = Mapper.Map>(mails)
+ };
+ }
+}
+#endif
diff --git a/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailViaImapQuery.cs b/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailViaImapQuery.cs
deleted file mode 100644
index e6e2454..0000000
--- a/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailViaImapQuery.cs
+++ /dev/null
@@ -1,58 +0,0 @@
-#if NET
-using AutoMapper;
-using DigitalData.MessagingService.Application.Common.Dto;
-using DigitalData.MessagingService.Application.Common.Dto.MailSearch;
-using DigitalData.MessagingService.Application.Common.Interfaces;
-using DigitalData.MessagingService.Application.Common.Interfaces.Repositories;
-using DigitalData.MessagingService.Application.EmailAccounts.Queries;
-using DigitalData.MessagingService.Domain.Entities;
-using DigitalData.MessagingService.Domain.Exceptions;
-using MediatR;
-using Microsoft.Extensions.Logging;
-
-namespace DigitalData.MessagingService.Application.EmailReceiving.Queries;
-
-///
-/// Query to fetch emails from an IMAP mailbox.
-///
-public record ReadEmailViaImapQuery : IRequest
-{
- ///
- /// Identifies the email account to use.
- ///
- public required GetEmailAccountQuery Account { get; init; }
-
- ///
- /// Mail query used to filter and limit the emails retrieved.
- ///
- public MailSearchFilter Mail { get; init; } = new();
-}
-
-public class ReadEmailViaImapQueryHandler(IMapper Mapper, ILogger Logger, IRepository EmailAccountRepo, IReceivedEmailRepository MailRepo, IImapEmailService imapEmailService) : IRequestHandler
-{
- public async Task Handle(ReadEmailViaImapQuery request, CancellationToken cancellationToken)
- {
- var accounts = await EmailAccountRepo.FindAsync(request.Account.Id is int id ? x => x.Id == id : x => x.Username == request.Account.Username, cancellationToken: cancellationToken);
-
- if (accounts.Count() > 1)
- Logger.LogWarning("Multiple email accounts found for the given criteria ({Criteria}). Returning the first one.", request.Account.Id is not null ? $"Id: {request.Account.Id}" : $"Username: {request.Account.Username}");
-
- var account = accounts.FirstOrDefault()
- ?? 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 BadRequestException(
- $"IMAP is not configured for account '{account.Username}' (Id: {account.Id}). Set ImapServer in EmailAccounts configuration.");
-
- var mails = await MailRepo.FindAsync(request.Mail, account, cancellationToken);
-
- var lastSync = imapEmailService.GetLastImapSyncDate(account.Id, request.Mail.Folder);
-
- return new ReadEmailQueryResponse
- {
- LastSync = lastSync,
- Emails = Mapper.Map>(mails)
- };
- }
-}
-#endif
\ No newline at end of file
diff --git a/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailViaOAuth2Query.cs b/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailViaOAuth2Query.cs
deleted file mode 100644
index 51bf42c..0000000
--- a/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailViaOAuth2Query.cs
+++ /dev/null
@@ -1,77 +0,0 @@
-#if NET
-using AutoMapper;
-using DigitalData.MessagingService.Application.Common.Dto;
-using DigitalData.MessagingService.Application.Common.Dto.MailSearch;
-using DigitalData.MessagingService.Application.Common.Interfaces;
-using DigitalData.MessagingService.Application.Common.Interfaces.Repositories;
-using DigitalData.MessagingService.Application.EmailAccounts.Queries;
-using DigitalData.MessagingService.Application.EmailReceiving.Queries;
-using DigitalData.MessagingService.Domain.Entities;
-using DigitalData.MessagingService.Domain.Exceptions;
-using MediatR;
-using Microsoft.Extensions.Logging;
-
-namespace DigitalData.MessagingService.Application.EmailReceiving.Queries;
-
-///
-/// Query to fetch emails from an IMAP mailbox using OAuth2 authentication.
-/// The account must have UseOAuth2 = true and valid OAuth2 credentials configured.
-///
-public record ReadEmailViaOAuth2Query : IRequest
-{
- ///
- /// Identifies the email account to use.
- ///
- public required GetEmailAccountQuery Account { get; init; }
-
- ///
- /// Mail query used to filter and limit the emails retrieved.
- ///
- public MailSearchFilter Mail { get; init; } = new();
-}
-
-public class ReadEmailViaOAuth2QueryHandler(
- IMapper Mapper,
- ILogger Logger,
- IRepository EmailAccountRepo,
- IReceivedEmailRepository MailRepo,
- IImapEmailService imapEmailService) : IRequestHandler
-{
- public async Task Handle(ReadEmailViaOAuth2Query request, CancellationToken cancellationToken)
- {
- var accounts = await EmailAccountRepo.FindAsync(
- request.Account.Id is int id ? x => x.Id == id : x => x.Username == request.Account.Username,
- cancellationToken: cancellationToken);
-
- if (accounts.Count() > 1)
- Logger.LogWarning("Multiple email accounts found ({Criteria}). Using first.",
- request.Account.Id is not null ? $"Id: {request.Account.Id}" : $"Username: {request.Account.Username}");
-
- var account = accounts.FirstOrDefault()
- ?? throw new NotFoundException($"No email account found (Id: {request.Account.Id}, Username: {request.Account.Username}).");
-
- if (!account.UseOAuth2)
- throw new BadRequestException(
- $"Account '{account.Username}' (Id: {account.Id}) is not configured for OAuth2. Set UseOAuth2 = true.");
-
- if (string.IsNullOrWhiteSpace(account.OAuth2ClientId) ||
- string.IsNullOrWhiteSpace(account.OAuth2ClientSecret))
- throw new BadRequestException(
- $"OAuth2 credentials (ClientId, ClientSecret) are not configured for account '{account.Username}'.");
-
- if (string.IsNullOrWhiteSpace(account.ImapServer))
- throw new BadRequestException(
- $"IMAP is not configured for account '{account.Username}' (Id: {account.Id}). Set ImapServer in EmailAccounts configuration.");
-
- var mails = await MailRepo.FindAsync(request.Mail, account, cancellationToken);
-
- var lastSync = imapEmailService.GetLastImapSyncDate(account.Id, request.Mail.Folder);
-
- return new ReadEmailQueryResponse
- {
- LastSync = lastSync,
- Emails = Mapper.Map>(mails)
- };
- }
-}
-#endif
diff --git a/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailViaPop3Query.cs b/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailViaPop3Query.cs
deleted file mode 100644
index 0f3bee0..0000000
--- a/src/core/DigitalData.MessagingService.Application/EmailReceiving/Queries/ReadEmailViaPop3Query.cs
+++ /dev/null
@@ -1,74 +0,0 @@
-#if NET
-using AutoMapper;
-using DigitalData.MessagingService.Application.Common.Dto;
-using DigitalData.MessagingService.Application.Common.Dto.MailSearch;
-using DigitalData.MessagingService.Application.Common.Interfaces;
-using DigitalData.MessagingService.Application.Common.Interfaces.Repositories;
-using DigitalData.MessagingService.Application.EmailAccounts.Queries;
-using DigitalData.MessagingService.Application.EmailReceiving.Queries;
-using DigitalData.MessagingService.Domain.Entities;
-using DigitalData.MessagingService.Domain.Exceptions;
-using MediatR;
-using Microsoft.Extensions.Logging;
-
-namespace DigitalData.MessagingService.Application.EmailReceiving.Queries;
-
-///
-/// Query to fetch emails from a POP3 mailbox.
-/// Triggers an on-demand sync and returns stored results filtered by .
-///
-public record ReadEmailViaPop3Query : IRequest
-{
- ///
- /// Identifies the email account to use.
- ///
- public required GetEmailAccountQuery Account { get; init; }
-
- ///
- /// Mail query used to filter and limit the emails retrieved from local storage.
- /// Note: POP3 has no folder concept — all messages are stored under "INBOX".
- ///
- public MailSearchFilter Mail { get; init; } = new();
-}
-
-public class ReadEmailViaPop3QueryHandler(
- IMapper Mapper,
- ILogger Logger,
- IRepository EmailAccountRepo,
- IReceivedEmailRepository MailRepo,
- IPop3EmailService pop3EmailService) : IRequestHandler
-{
- public async Task Handle(ReadEmailViaPop3Query request, CancellationToken cancellationToken)
- {
- var accounts = await EmailAccountRepo.FindAsync(
- request.Account.Id is int id ? x => x.Id == id : x => x.Username == request.Account.Username,
- cancellationToken: cancellationToken);
-
- if (accounts.Count() > 1)
- Logger.LogWarning("Multiple email accounts found ({Criteria}). Using first.",
- request.Account.Id is not null ? $"Id: {request.Account.Id}" : $"Username: {request.Account.Username}");
-
- var account = accounts.FirstOrDefault()
- ?? throw new NotFoundException($"No email account found (Id: {request.Account.Id}, Username: {request.Account.Username}).");
-
- if (string.IsNullOrWhiteSpace(account.Pop3Server))
- throw new BadRequestException(
- $"POP3 is not configured for account '{account.Username}' (Id: {account.Id}). Set Pop3Server in EmailAccounts configuration.");
-
- // Trigger on-demand POP3 sync before querying local storage
- await pop3EmailService.SyncEmailsAsync(account, cancellationToken);
-
- // POP3 has no folder concept — always query INBOX
- var filter = request.Mail with { Folder = "INBOX" };
- var mails = await MailRepo.FindAsync(filter, account, cancellationToken);
-
- var lastSync = pop3EmailService.GetLastPop3SyncDate(account.Id);
-
- return new ReadEmailQueryResponse
- {
- LastSync = lastSync,
- Emails = Mapper.Map>(mails)
- };
- }
-}
-#endif
diff --git a/src/core/DigitalData.MessagingService.Application/EmailReceiving/Validators/FetchEmailsQueryValidator.cs b/src/core/DigitalData.MessagingService.Application/EmailReceiving/Validators/FetchEmailsQueryValidator.cs
index e9e7919..4b49283 100644
--- a/src/core/DigitalData.MessagingService.Application/EmailReceiving/Validators/FetchEmailsQueryValidator.cs
+++ b/src/core/DigitalData.MessagingService.Application/EmailReceiving/Validators/FetchEmailsQueryValidator.cs
@@ -5,9 +5,9 @@ using FluentValidation;
namespace DigitalData.MessagingService.Application.EmailReceiving.Validators;
///
-/// Validates a before it is handled by .
+/// Validates a before it is handled by .
///
-public class FetchEmailsQueryValidator : AbstractValidator
+public class FetchEmailsQueryValidator : AbstractValidator
{
public FetchEmailsQueryValidator()
{
diff --git a/src/presentation/DigitalData.MessagingService.API/Controllers/EmailController.cs b/src/presentation/DigitalData.MessagingService.API/Controllers/EmailController.cs
index ba7b2fc..c39d8c1 100644
--- a/src/presentation/DigitalData.MessagingService.API/Controllers/EmailController.cs
+++ b/src/presentation/DigitalData.MessagingService.API/Controllers/EmailController.cs
@@ -1,37 +1,71 @@
-using DigitalData.MessagingService.Application.EmailSending.Commands;
+using DigitalData.MessagingService.Application.Common.Dto;
using DigitalData.MessagingService.Application.EmailReceiving.Queries;
+using DigitalData.MessagingService.Application.EmailSending.Commands;
using MediatR;
using Microsoft.AspNetCore.Mvc;
-using DigitalData.MessagingService.Application.Common.Dto;
namespace DigitalData.MessagingService.API.Controllers;
///
-/// Email sending API controller.
-/// Enqueues outgoing emails to RabbitMQ for async processing.
+/// Email API controller.
+/// Handles sending and receiving emails across all supported protocols and auth methods.
///
[ApiController]
[Route("api/[controller]")]
public class EmailController(IMediator mediator) : ControllerBase
{
///
- ///
+ /// Controls which field(s) to return from a fetch-emails response.
///
public enum OnlyFilter
{
- ///
- ///
- ///
+ /// Returns only the HTML body of the first matching email.
HtmlBody,
- ///
- ///
- ///
+ /// Returns only the UID list of matching emails.
Uid,
}
+ #region Receive
+ ///
+ /// Fetch emails from an email account.
+ /// The protocol (IMAP, POP3) and authentication method (basic, OAuth2) are determined
+ /// by the IncomingProtocol field configured on the account.
+ ///
+ /// Query parameters for filtering and fetching emails.
+ /// Optional filter to return only specific fields.
+ /// Cancellation token.
+ /// HTTP 200 with list of received emails.
+ [HttpGet]
+ [ProducesResponseType(StatusCodes.Status200OK)]
+ [ProducesResponseType(StatusCodes.Status400BadRequest)]
+ [ProducesResponseType(StatusCodes.Status404NotFound)]
+ public async Task FetchEmails(
+ [FromQuery] ReadEmailQuery query,
+ [FromQuery] OnlyFilter? only = null,
+ CancellationToken cancellationToken = default)
+ {
+ var res = await mediator.Send(query, cancellationToken);
+
+ if (!res.Emails.Any())
+ return NotFound("No emails found matching the specified criteria.");
+
+ if (only == OnlyFilter.HtmlBody)
+ {
+ if (res.Emails.FirstOrDefault()?.HtmlBody is string htmlBody)
+ return Content(htmlBody, "text/html");
+ else
+ return NotFound();
+ }
+ else if (only == OnlyFilter.Uid)
+ return Ok(res.Emails.Select(e => e.Uid).ToList());
+ else
+ return Ok(res);
+ }
+ #endregion Receive
+
#region SMTP Send
///
- /// Send an email, optionally with file attachments.
+ /// Send an email via SMTP, optionally with file attachments.
/// Omit the attachments field for a plain send.
///
/// Email fields as form values
@@ -53,66 +87,8 @@ public class EmailController(IMediator mediator) : ControllerBase
var eventId = await mediator.Send(commandWithAttachments, cancellationToken);
return Accepted(new { Id = eventId });
}
-
- private static async Task> BuildAttachmentsAsync(
- IFormFileCollection? files,
- CancellationToken cancellationToken)
- {
- if (files is null || files.Count == 0)
- return [];
-
- var result = new List(files.Count);
-
- foreach (var file in files)
- {
- using var ms = new MemoryStream();
- await file.CopyToAsync(ms, cancellationToken);
-
- result.Add(new EmailAttachmentDto
- {
- FileName = file.FileName,
- Content = ms.ToArray(),
- ContentType = file.ContentType
- });
- }
-
- return result;
- }
#endregion SMTP Send
- #region IMAP Receive
- ///
- /// Fetch emails from an IMAP mailbox.
- ///
- /// Query parameters for filtering and fetching emails from the IMAP mailbox.
- ///
- /// Cancellation token.
- /// HTTP 200 with list of received emails.
- [HttpGet]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task FetchEmails([FromQuery] ReadEmailViaImapQuery query, [FromQuery] OnlyFilter? only = null, CancellationToken cancellationToken = default)
- {
- var res = await mediator.Send(query, cancellationToken);
-
- if(!res.Emails.Any())
- return NotFound("No emails found matching the specified criteria.");
-
- if (only == OnlyFilter.HtmlBody)
- {
- if (res.Emails.FirstOrDefault()?.HtmlBody is string htmlBody)
- return Content(htmlBody, "text/html");
- else
- return NotFound();
- }
- else if (only == OnlyFilter.Uid)
- return Ok(res.Emails.Select(e => e.Uid).ToList());
- else
- return Ok(res);
- }
- #endregion IMAP Receive
-
#region IMAP Send
///
/// Send an email using IMAP account credentials (via RabbitMQ queue).
@@ -139,43 +115,6 @@ public class EmailController(IMediator mediator) : ControllerBase
}
#endregion IMAP Send
- #region POP3 Receive
- ///
- /// Fetch emails from a POP3 mailbox.
- /// Triggers an on-demand POP3 sync before returning results.
- ///
- /// Query parameters for filtering and fetching emails from the POP3 mailbox.
- /// Optional filter to return only specific fields.
- /// Cancellation token.
- /// HTTP 200 with list of received emails.
- [HttpGet("pop3")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task FetchEmailsViaPop3(
- [FromQuery] ReadEmailViaPop3Query query,
- [FromQuery] OnlyFilter? only = null,
- CancellationToken cancellationToken = default)
- {
- var res = await mediator.Send(query, cancellationToken);
-
- if (!res.Emails.Any())
- return NotFound("No emails found matching the specified criteria.");
-
- if (only == OnlyFilter.HtmlBody)
- {
- if (res.Emails.FirstOrDefault()?.HtmlBody is string htmlBody)
- return Content(htmlBody, "text/html");
- else
- return NotFound();
- }
- else if (only == OnlyFilter.Uid)
- return Ok(res.Emails.Select(e => e.Uid).ToList());
- else
- return Ok(res);
- }
- #endregion POP3 Receive
-
#region OAuth2 Send
///
/// Send an email via SMTP using OAuth2 authentication (via RabbitMQ queue).
@@ -202,40 +141,28 @@ public class EmailController(IMediator mediator) : ControllerBase
}
#endregion OAuth2 Send
- #region OAuth2 Receive
- ///
- /// Fetch emails from an IMAP mailbox using OAuth2 authentication.
- /// The account must have UseOAuth2 = true and valid OAuth2 credentials configured.
- ///
- /// Query parameters for filtering and fetching emails.
- /// Optional filter to return only specific fields.
- /// Cancellation token.
- /// HTTP 200 with list of received emails.
- [HttpGet("oauth2/imap")]
- [ProducesResponseType(StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task FetchEmailsViaOAuth2(
- [FromQuery] ReadEmailViaOAuth2Query query,
- [FromQuery] OnlyFilter? only = null,
- CancellationToken cancellationToken = default)
+ private static async Task> BuildAttachmentsAsync(
+ IFormFileCollection? files,
+ CancellationToken cancellationToken)
{
- var res = await mediator.Send(query, cancellationToken);
+ if (files is null || files.Count == 0)
+ return [];
- if (!res.Emails.Any())
- return NotFound("No emails found matching the specified criteria.");
+ var result = new List(files.Count);
- if (only == OnlyFilter.HtmlBody)
+ foreach (var file in files)
{
- if (res.Emails.FirstOrDefault()?.HtmlBody is string htmlBody)
- return Content(htmlBody, "text/html");
- else
- return NotFound();
+ using var ms = new MemoryStream();
+ await file.CopyToAsync(ms, cancellationToken);
+
+ result.Add(new EmailAttachmentDto
+ {
+ FileName = file.FileName,
+ Content = ms.ToArray(),
+ ContentType = file.ContentType
+ });
}
- else if (only == OnlyFilter.Uid)
- return Ok(res.Emails.Select(e => e.Uid).ToList());
- else
- return Ok(res);
+
+ return result;
}
- #endregion OAuth2 Receive
}
diff --git a/src/presentation/DigitalData.MessagingService.API/DigitalData.MessagingService.API.csproj b/src/presentation/DigitalData.MessagingService.API/DigitalData.MessagingService.API.csproj
index 688810a..dc2a075 100644
--- a/src/presentation/DigitalData.MessagingService.API/DigitalData.MessagingService.API.csproj
+++ b/src/presentation/DigitalData.MessagingService.API/DigitalData.MessagingService.API.csproj
@@ -10,9 +10,9 @@
Digital Data GmbH
Digital Data GmbH
DigitalData.MessagingService.API
- 1.0.0-beta
- 1.0.0.0
- 1.0.0.0
+ 2.0.0-beta
+ 2.0.0.0
+ 2.0.0.0
Copyright © 2026 Digital Data GmbH. All rights reserved.
bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml