diff --git a/src/DigitalData.EmailProfiler.API/Controllers/EmailAccountsController.cs b/src/DigitalData.EmailProfiler.API/Controllers/EmailAccountsController.cs
deleted file mode 100644
index fc6b0b8..0000000
--- a/src/DigitalData.EmailProfiler.API/Controllers/EmailAccountsController.cs
+++ /dev/null
@@ -1,70 +0,0 @@
-using DigitalData.EmailProfiler.Application.Common.Dtos;
-using DigitalData.EmailProfiler.Application.Common.Interfaces;
-using DigitalData.EmailProfiler.Application.EmailAccounts.Commands;
-using DigitalData.EmailProfiler.Application.EmailAccounts.Queries;
-using MediatR;
-using Microsoft.AspNetCore.Mvc;
-
-namespace DigitalData.EmailProfiler.API.Controllers;
-
-///
-/// Email accounts management API controller
-///
-[ApiController]
-[Route("api/[controller]")]
-public class EmailAccountsController(
- IMediator mediator,
- ICommandPublisher commandPublisher) : ControllerBase
-{
- ///
- /// Get all email accounts
- ///
- [HttpGet]
- [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)]
- public async Task GetAll(CancellationToken cancellationToken)
- {
- var query = new GetEmailAccountsQuery();
- var result = await mediator.Send(query, cancellationToken);
- return Ok(result);
- }
-
- ///
- /// Get email account by ID
- ///
- [HttpGet("{id:int}")]
- [ProducesResponseType(typeof(EmailAccountDto), StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task GetById(int id, CancellationToken cancellationToken)
- {
- var query = new GetEmailAccountByIdQuery(id);
- var result = await mediator.Send(query, cancellationToken);
-
- if (result == null)
- return NotFound(new { Message = $"Email account with ID {id} not found" });
-
- return Ok(result);
- }
-
- ///
- /// Create new email account (async via RabbitMQ)
- ///
- [HttpPost]
- [ProducesResponseType(StatusCodes.Status202Accepted)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- public async Task Create(
- [FromBody] CreateEmailAccountCommand command,
- CancellationToken cancellationToken)
- {
- // Publish command to RabbitMQ for async processing
- await commandPublisher.PublishAsync(command, cancellationToken);
-
- return Accepted(new
- {
- Message = "Email account creation request queued for processing",
- AccountName = command.AccountName
- });
- }
-
- // Note: Update and Delete operations can be added similarly
- // For now, we focus on Create as the main use case
-}
diff --git a/src/DigitalData.EmailProfiler.API/Controllers/EmailHistoryController.cs b/src/DigitalData.EmailProfiler.API/Controllers/EmailHistoryController.cs
deleted file mode 100644
index 0b62f32..0000000
--- a/src/DigitalData.EmailProfiler.API/Controllers/EmailHistoryController.cs
+++ /dev/null
@@ -1,57 +0,0 @@
-using DigitalData.EmailProfiler.Application.Common.Dtos.EmailHistories;
-using DigitalData.EmailProfiler.Application.EmailHistories.Queries;
-using MediatR;
-using Microsoft.AspNetCore.Mvc;
-
-namespace DigitalData.EmailProfiler.API.Controllers;
-
-///
-/// Email history API controller (read-only)
-///
-[ApiController]
-[Route("api/[controller]")]
-public class EmailHistoryController(IMediator mediator) : ControllerBase
-{
- ///
- /// Get email history by profile ID with pagination
- ///
- [HttpGet("profile/{profileId:int}")]
- [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)]
- public async Task GetByProfile(
- int profileId,
- [FromQuery] int pageNumber = 1,
- [FromQuery] int pageSize = 50,
- CancellationToken cancellationToken = default)
- {
- var query = new GetEmailHistoryByProfileQuery(profileId, pageNumber, pageSize);
- var result = await mediator.Send(query, cancellationToken);
-
- return Ok(new
- {
- ProfileId = profileId,
- PageNumber = pageNumber,
- PageSize = pageSize,
- Data = result
- });
- }
-
- ///
- /// Get email history by ID
- ///
- [HttpGet("{id:int}")]
- [ProducesResponseType(typeof(EmailHistoryDto), StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task GetById(int id, CancellationToken cancellationToken)
- {
- var query = new GetEmailHistoryByIdQuery(id);
- var result = await mediator.Send(query, cancellationToken);
-
- if (result == null)
- return NotFound(new { Message = $"Email history with ID {id} not found" });
-
- return Ok(result);
- }
-
- // Note: Email history is typically managed by ProcessEmailCommand
- // No direct CREATE/UPDATE/DELETE endpoints needed
-}
diff --git a/src/DigitalData.EmailProfiler.API/Controllers/EmailProfilesController.cs b/src/DigitalData.EmailProfiler.API/Controllers/EmailProfilesController.cs
deleted file mode 100644
index 1930a98..0000000
--- a/src/DigitalData.EmailProfiler.API/Controllers/EmailProfilesController.cs
+++ /dev/null
@@ -1,123 +0,0 @@
-using DigitalData.EmailProfiler.Application.Common.Dtos;
-using DigitalData.EmailProfiler.Application.Common.Interfaces;
-using DigitalData.EmailProfiler.Application.EmailProfiles.Commands;
-using DigitalData.EmailProfiler.Application.EmailProfiles.Queries;
-using MediatR;
-using Microsoft.AspNetCore.Mvc;
-
-namespace DigitalData.EmailProfiler.API.Controllers;
-
-///
-/// Email profiles management API controller
-///
-[ApiController]
-[Route("api/[controller]")]
-public class EmailProfilesController(
- IMediator mediator,
- ICommandPublisher commandPublisher) : ControllerBase
-{
- ///
- /// Get all email profiles
- ///
- [HttpGet]
- [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)]
- public async Task GetAll(CancellationToken cancellationToken)
- {
- var query = new GetEmailProfilesQuery();
- var result = await mediator.Send(query, cancellationToken);
- return Ok(result);
- }
-
- ///
- /// Get email profile by ID
- ///
- [HttpGet("{id:int}")]
- [ProducesResponseType(typeof(EmailProfileDto), StatusCodes.Status200OK)]
- [ProducesResponseType(StatusCodes.Status404NotFound)]
- public async Task GetById(int id, CancellationToken cancellationToken)
- {
- var query = new GetEmailProfileByIdQuery(id);
- var result = await mediator.Send(query, cancellationToken);
-
- if (result == null)
- return NotFound(new { Message = $"Email profile with ID {id} not found" });
-
- return Ok(result);
- }
-
- ///
- /// Get all active email profiles
- ///
- [HttpGet("active")]
- [ProducesResponseType(typeof(IEnumerable), StatusCodes.Status200OK)]
- public async Task GetActive(CancellationToken cancellationToken)
- {
- var query = new GetActiveEmailProfilesQuery();
- var result = await mediator.Send(query, cancellationToken);
- return Ok(result);
- }
-
- ///
- /// Create new email profile (async via RabbitMQ)
- ///
- [HttpPost]
- [ProducesResponseType(StatusCodes.Status202Accepted)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- public async Task Create(
- [FromBody] CreateEmailProfileCommand command,
- CancellationToken cancellationToken)
- {
- // Publish command to RabbitMQ for async processing
- await commandPublisher.PublishAsync(command, cancellationToken);
-
- return Accepted(new
- {
- Message = "Email profile creation request queued for processing",
- ProfileName = command.ProfileName
- });
- }
-
- ///
- /// Update email profile (async via RabbitMQ)
- ///
- [HttpPut("{id:int}")]
- [ProducesResponseType(StatusCodes.Status202Accepted)]
- [ProducesResponseType(StatusCodes.Status400BadRequest)]
- public async Task Update(
- int id,
- [FromBody] UpdateEmailProfileCommand command,
- CancellationToken cancellationToken)
- {
- // Ensure ID matches route
- if (id != command.Id)
- return BadRequest(new { Message = "Route ID does not match command ID" });
-
- // Publish command to RabbitMQ for async processing
- await commandPublisher.PublishAsync(command, cancellationToken);
-
- return Accepted(new
- {
- Message = "Email profile update request queued for processing",
- Id = command.Id
- });
- }
-
- ///
- /// Delete email profile (async via RabbitMQ)
- ///
- [HttpDelete("{id:int}")]
- [ProducesResponseType(StatusCodes.Status202Accepted)]
- public async Task Delete(int id, CancellationToken cancellationToken)
- {
- var command = new DeleteEmailProfileCommand(id);
-
- // Publish command to RabbitMQ for async processing
- await commandPublisher.PublishAsync(command, cancellationToken);
-
- return Accepted(new
- {
- Message = "Email profile deletion request queued for processing",
- Id = id
- });
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailAccounts/Commands/CreateEmailAccountCommand.cs b/src/DigitalData.EmailProfiler.Application/EmailAccounts/Commands/CreateEmailAccountCommand.cs
deleted file mode 100644
index f711e5f..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailAccounts/Commands/CreateEmailAccountCommand.cs
+++ /dev/null
@@ -1,36 +0,0 @@
-using DigitalData.EmailProfiler.Application.Interfaces.Repositories;
-using DigitalData.EmailProfiler.Domain.Entities;
-using MediatR;
-
-namespace DigitalData.EmailProfiler.Application.EmailAccounts.Commands;
-
-///
-/// Command to create a new email account.
-///
-public record CreateEmailAccountCommand : IRequest
-{
- public string AccountName { get; init; } = string.Empty;
- public string Username { get; init; } = string.Empty;
- public string ImapServer { get; init; } = string.Empty;
- public int ImapPort { get; init; } = 993;
- public bool ImapUseSsl { get; init; } = true;
- public string SmtpServer { get; init; } = string.Empty;
- public int SmtpPort { get; init; } = 587;
- public bool SmtpUseSsl { get; init; } = true;
- public bool UseOAuth2 { get; init; }
- public string? EncryptedPassword { get; init; } // Already encrypted by client
- public string? TenantId { get; init; }
- public string? ClientId { get; init; }
- public string? EncryptedClientSecret { get; init; } // Already encrypted by client
- public bool IsActive { get; init; } = true;
-}
-
-public class CreateEmailAccountCommandHandler(IRepository repository)
- : IRequestHandler
-{
- public async Task Handle(CreateEmailAccountCommand request, CancellationToken cancellationToken)
- {
- var account = await repository.CreateAsync(request, cancellationToken);
- return account.Id;
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailAccounts/Queries/GetEmailAccountByIdQuery.cs b/src/DigitalData.EmailProfiler.Application/EmailAccounts/Queries/GetEmailAccountByIdQuery.cs
deleted file mode 100644
index 3f92296..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailAccounts/Queries/GetEmailAccountByIdQuery.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using AutoMapper;
-using DigitalData.EmailProfiler.Application.Common.Dtos;
-using DigitalData.EmailProfiler.Application.Interfaces.Repositories;
-using DigitalData.EmailProfiler.Domain.Entities;
-using MediatR;
-
-namespace DigitalData.EmailProfiler.Application.EmailAccounts.Queries;
-
-///
-/// Query to get email account by ID.
-///
-public record GetEmailAccountByIdQuery(int Id) : IRequest;
-
-public class GetEmailAccountByIdQueryHandler(
- IRepository repository,
- IMapper mapper)
- : IRequestHandler
-{
- public async Task Handle(
- GetEmailAccountByIdQuery request,
- CancellationToken cancellationToken)
- {
- var account = await repository.GetByIdAsync(request.Id, cancellationToken);
- return account != null ? mapper.Map(account) : null;
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailAccounts/Queries/GetEmailAccountsQuery.cs b/src/DigitalData.EmailProfiler.Application/EmailAccounts/Queries/GetEmailAccountsQuery.cs
deleted file mode 100644
index 7bed192..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailAccounts/Queries/GetEmailAccountsQuery.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using AutoMapper;
-using DigitalData.EmailProfiler.Application.Common.Dtos;
-using DigitalData.EmailProfiler.Application.Interfaces.Repositories;
-using DigitalData.EmailProfiler.Domain.Entities;
-using MediatR;
-
-namespace DigitalData.EmailProfiler.Application.EmailAccounts.Queries;
-
-///
-/// Query to get all email accounts.
-///
-public record GetEmailAccountsQuery : IRequest>;
-
-public class GetEmailAccountsQueryHandler(
- IRepository repository,
- IMapper mapper)
- : IRequestHandler>
-{
- public async Task> Handle(
- GetEmailAccountsQuery request,
- CancellationToken cancellationToken)
- {
- var accounts = await repository.GetAllAsync(cancellationToken);
- return mapper.Map>(accounts);
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailAccounts/Validators/CreateEmailAccountCommandValidator.cs b/src/DigitalData.EmailProfiler.Application/EmailAccounts/Validators/CreateEmailAccountCommandValidator.cs
deleted file mode 100644
index baf1b0b..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailAccounts/Validators/CreateEmailAccountCommandValidator.cs
+++ /dev/null
@@ -1,56 +0,0 @@
-using DigitalData.EmailProfiler.Application.EmailAccounts.Commands;
-using FluentValidation;
-
-namespace DigitalData.EmailProfiler.Application.EmailAccounts.Validators;
-
-///
-/// Validator for CreateEmailAccountCommand.
-///
-public class CreateEmailAccountCommandValidator : AbstractValidator
-{
- public CreateEmailAccountCommandValidator()
- {
- RuleFor(x => x.AccountName)
- .NotEmpty().WithMessage("Account name is required")
- .MaximumLength(100).WithMessage("Account name must not exceed 100 characters");
-
- RuleFor(x => x.Username)
- .NotEmpty().WithMessage("Username is required")
- .MaximumLength(200).WithMessage("Username must not exceed 200 characters")
- .EmailAddress().WithMessage("Username must be a valid email address");
-
- RuleFor(x => x.ImapServer)
- .NotEmpty().WithMessage("IMAP server is required")
- .MaximumLength(200).WithMessage("IMAP server must not exceed 200 characters");
-
- RuleFor(x => x.ImapPort)
- .GreaterThan(0).WithMessage("IMAP port must be greater than 0")
- .LessThanOrEqualTo(65535).WithMessage("IMAP port must not exceed 65535");
-
- RuleFor(x => x.SmtpServer)
- .NotEmpty().WithMessage("SMTP server is required")
- .MaximumLength(200).WithMessage("SMTP server must not exceed 200 characters");
-
- RuleFor(x => x.SmtpPort)
- .GreaterThan(0).WithMessage("SMTP port must be greater than 0")
- .LessThanOrEqualTo(65535).WithMessage("SMTP port must not exceed 65535");
-
- // OAuth2 validation
- RuleFor(x => x.TenantId)
- .NotEmpty().WithMessage("Tenant ID is required for OAuth2")
- .When(x => x.UseOAuth2);
-
- RuleFor(x => x.ClientId)
- .NotEmpty().WithMessage("Client ID is required for OAuth2")
- .When(x => x.UseOAuth2);
-
- RuleFor(x => x.EncryptedClientSecret)
- .NotEmpty().WithMessage("Client secret is required for OAuth2")
- .When(x => x.UseOAuth2);
-
- // Password validation (non-OAuth2)
- RuleFor(x => x.EncryptedPassword)
- .NotEmpty().WithMessage("Password is required")
- .When(x => !x.UseOAuth2);
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailHistories/Queries/GetEmailHistoryByIdQuery.cs b/src/DigitalData.EmailProfiler.Application/EmailHistories/Queries/GetEmailHistoryByIdQuery.cs
deleted file mode 100644
index ff1bf8c..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailHistories/Queries/GetEmailHistoryByIdQuery.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using AutoMapper;
-using DigitalData.EmailProfiler.Application.Common.Dtos.EmailHistories;
-using DigitalData.EmailProfiler.Application.Interfaces.Repositories;
-using MediatR;
-
-namespace DigitalData.EmailProfiler.Application.EmailHistories.Queries;
-
-///
-/// Query to get email history by ID with attachments.
-///
-public record GetEmailHistoryByIdQuery(int Id) : IRequest;
-
-public class GetEmailHistoryByIdQueryHandler(
- IEmailHistoryRepository repository,
- IMapper mapper)
- : IRequestHandler
-{
- public async Task Handle(
- GetEmailHistoryByIdQuery request,
- CancellationToken cancellationToken)
- {
- var history = await repository.GetWithAttachmentsAsync(request.Id, cancellationToken);
- return history != null ? mapper.Map(history) : null;
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailHistories/Queries/GetEmailHistoryByProfileQuery.cs b/src/DigitalData.EmailProfiler.Application/EmailHistories/Queries/GetEmailHistoryByProfileQuery.cs
deleted file mode 100644
index e222954..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailHistories/Queries/GetEmailHistoryByProfileQuery.cs
+++ /dev/null
@@ -1,48 +0,0 @@
-using AutoMapper;
-using DigitalData.EmailProfiler.Application.Common.Dtos.EmailHistories;
-using DigitalData.EmailProfiler.Application.Interfaces.Repositories;
-using MediatR;
-
-namespace DigitalData.EmailProfiler.Application.EmailHistories.Queries;
-
-///
-/// Query to get email history by profile with pagination.
-///
-public record GetEmailHistoryByProfileQuery(
- int ProfileId,
- int PageNumber = 1,
- int PageSize = 50) : IRequest;
-
-///
-/// Paged result for email history.
-///
-public record EmailHistoryPagedResult(
- IEnumerable Items,
- int TotalCount,
- int PageNumber,
- int PageSize);
-
-public class GetEmailHistoryByProfileQueryHandler(
- IEmailHistoryRepository repository,
- IMapper mapper)
- : IRequestHandler
-{
- public async Task Handle(
- GetEmailHistoryByProfileQuery request,
- CancellationToken cancellationToken)
- {
- var (items, totalCount) = await repository.GetByProfileIdAsync(
- request.ProfileId,
- request.PageNumber,
- request.PageSize,
- cancellationToken);
-
- var dtos = mapper.Map>(items);
-
- return new EmailHistoryPagedResult(
- dtos,
- totalCount,
- request.PageNumber,
- request.PageSize);
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailProcessing/Commands/ProcessEmailCommand.cs b/src/DigitalData.EmailProfiler.Application/EmailProcessing/Commands/ProcessEmailCommand.cs
deleted file mode 100644
index e48f982..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailProcessing/Commands/ProcessEmailCommand.cs
+++ /dev/null
@@ -1,180 +0,0 @@
-using DigitalData.EmailProfiler.Application.Common.Dtos.EmailAttachments;
-using DigitalData.EmailProfiler.Application.Common.Dtos.EmailHistories;
-using DigitalData.EmailProfiler.Application.Interfaces.Repositories;
-using DigitalData.EmailProfiler.Application.Interfaces.Services;
-using DigitalData.EmailProfiler.Domain.Entities;
-using DigitalData.EmailProfiler.Domain.Enums;
-using DigitalData.EmailProfiler.Domain.Events;
-using DigitalData.EmailProfiler.Domain.Exceptions;
-using DigitalData.EmailProfiler.Domain.Services;
-using MediatR;
-
-namespace DigitalData.EmailProfiler.Application.EmailProcessing.Commands;
-
-///
-/// Command to process a single email from a profile.
-/// This is the core email processing logic.
-///
-public record ProcessEmailCommand : IRequest
-{
- public int ProfileId { get; init; }
- public string MessageId { get; init; } = string.Empty;
- public string Sender { get; init; } = string.Empty;
- public DateTime ReceivedDate { get; init; }
- public string Subject { get; init; } = string.Empty;
- public string? BodyText { get; init; }
- public string? BodyHtml { get; init; }
- public List Attachments { get; init; } = new();
-}
-
-///
-/// Attachment data for email processing.
-///
-public record AttachmentData(
- string FileName,
- byte[] Content,
- string ContentType,
- long SizeBytes);
-
-public class ProcessEmailCommandHandler(
- IEmailProfileRepository profileRepository,
- IEmailHistoryRepository historyRepository,
- IRepository attachmentRepository,
- IPublisher publisher,
- MessageIdGenerator messageIdGenerator,
- IPdfProcessingService pdfService,
- IDmsService dmsService)
- : IRequestHandler
-{
- public async Task Handle(ProcessEmailCommand request, CancellationToken cancellationToken)
- {
- // 1. Get profile with related entities
- var profile = await profileRepository.GetWithRelatedEntitiesAsync(request.ProfileId, cancellationToken)
- ?? throw new DomainException($"Profile with ID {request.ProfileId} not found");
-
- // 2. Generate message ID hash for duplicate detection
- var messageId = messageIdGenerator.Generate(
- request.MessageId,
- request.Sender,
- request.ReceivedDate,
- request.Subject);
-
- // 3. Check for duplicates
- var isDuplicate = await historyRepository.IsDuplicateAsync(messageId.Hash, cancellationToken);
- if (isDuplicate)
- {
- throw new ValidationException("Email already processed (duplicate detected)", ErrorCode.DuplicateMessageId);
- }
-
- // 4. Create email history record using DTO approach
- var historyDto = new CreateEmailHistoryDto(
- ProfileId: profile.Id,
- MessageIdHash: messageId.Hash,
- OriginalMessageId: request.MessageId,
- SenderAddress: request.Sender,
- EmailDate: request.ReceivedDate,
- Subject: request.Subject,
- EmailBodyText: request.BodyText,
- EmailBodyHtml: request.BodyHtml);
-
- var createdHistory = await historyRepository.CreateAsync(historyDto, cancellationToken);
-
- try
- {
- // 5. Process attachments
- foreach (var attachmentData in request.Attachments)
- {
- var attachmentDto = new CreateEmailAttachmentDto(
- EmailHistoryId: createdHistory.Id,
- OriginalFileName: attachmentData.FileName,
- SavedFileName: attachmentData.FileName, // TODO: Generate unique name
- FilePath: string.Empty, // TODO: Save to disk and get path
- FileSize: attachmentData.SizeBytes,
- Extension: Path.GetExtension(attachmentData.FileName),
- ContentType: attachmentData.ContentType,
- Content: attachmentData.Content);
-
- var attachment = await attachmentRepository.CreateAsync(attachmentDto, cancellationToken);
-
- // Validate PDF attachments - need to retrieve and update the entity
- if (attachmentData.ContentType.Contains("pdf", StringComparison.OrdinalIgnoreCase))
- {
- using var stream = new MemoryStream(attachmentData.Content);
- var isValidPdf = await pdfService.IsValidPdfAsync(stream, cancellationToken);
-
- // For domain methods like MarkAsValid/MarkAsCorrupt, we need to get the entity
- var attachmentEntity = await attachmentRepository.GetByIdAsync(attachment.Id, cancellationToken);
- if (attachmentEntity != null)
- {
- if (isValidPdf)
- {
- attachmentEntity.MarkAsValid();
- }
- else
- {
- attachmentEntity.MarkAsCorrupt(ErrorCode.PdfStructureInvalid, "Invalid PDF structure");
- }
-
- // Update directly using UpdateSingleAsync for safety
- await attachmentRepository.UpdateSingleAsync(
- a => a.Id == attachmentEntity.Id,
- new UpdateEmailAttachmentStatusDto(
- Status: attachmentEntity.Status ?? string.Empty,
- ValidationErrorCode: attachmentEntity.ValidationErrorCode,
- ValidationErrorMessage: attachmentEntity.ValidationErrorMessage),
- cancellationToken);
- }
- }
- }
-
- // 6. Archive to DMS if configured
- if (profile.EmailProcess != null && profile.EmailProcess.EnableWindreamImport)
- {
- // TODO: Implement DMS archiving with indexing steps
- // This will be implemented based on ProcessSteps and IndexingSteps
- }
-
- // 7. Mark as processed - retrieve entity for domain method
- var historyEntity = await historyRepository.GetByIdAsync(createdHistory.Id, cancellationToken);
- if (historyEntity != null)
- {
- historyEntity.MarkAsProcessed();
- await historyRepository.UpdateSingleAsync(
- h => h.Id == historyEntity.Id,
- new UpdateEmailHistoryStatusDto(
- Status: historyEntity.Status ?? string.Empty,
- ProcessedDate: historyEntity.ProcessedDate),
- cancellationToken);
- }
-
- // 8. Publish domain event
- await publisher.Publish(
- new EmailProcessedEvent(
- createdHistory.Id,
- profile.Id,
- messageId.Hash,
- EmailStatus.Processed),
- cancellationToken);
-
- return createdHistory.Id;
- }
- catch (Exception ex)
- {
- // Mark as failed - retrieve entity for domain method
- var historyEntity = await historyRepository.GetByIdAsync(createdHistory.Id, cancellationToken);
- if (historyEntity != null)
- {
- historyEntity.MarkAsFailed(ErrorCode.AttachmentExtractionFailed, ex.Message);
- await historyRepository.UpdateSingleAsync(
- h => h.Id == historyEntity.Id,
- new UpdateEmailHistoryStatusDto(
- Status: historyEntity.Status ?? string.Empty,
- ErrorCodeValue: historyEntity.ErrorCodeValue,
- ErrorMessage: historyEntity.ErrorMessage),
- cancellationToken);
- }
-
- throw;
- }
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailProcessing/Validators/ProcessEmailCommandValidator.cs b/src/DigitalData.EmailProfiler.Application/EmailProcessing/Validators/ProcessEmailCommandValidator.cs
deleted file mode 100644
index a3989a9..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailProcessing/Validators/ProcessEmailCommandValidator.cs
+++ /dev/null
@@ -1,50 +0,0 @@
-using DigitalData.EmailProfiler.Application.EmailProcessing.Commands;
-using FluentValidation;
-
-namespace DigitalData.EmailProfiler.Application.EmailProcessing.Validators;
-
-///
-/// Validator for ProcessEmailCommand.
-///
-public class ProcessEmailCommandValidator : AbstractValidator
-{
- public ProcessEmailCommandValidator()
- {
- RuleFor(x => x.ProfileId)
- .GreaterThan(0).WithMessage("Profile ID must be greater than 0");
-
- RuleFor(x => x.MessageId)
- .NotEmpty().WithMessage("Message ID is required")
- .MaximumLength(500).WithMessage("Message ID must not exceed 500 characters");
-
- RuleFor(x => x.Sender)
- .NotEmpty().WithMessage("Sender is required")
- .MaximumLength(200).WithMessage("Sender must not exceed 200 characters")
- .EmailAddress().WithMessage("Sender must be a valid email address");
-
- RuleFor(x => x.Subject)
- .NotEmpty().WithMessage("Subject is required")
- .MaximumLength(500).WithMessage("Subject must not exceed 500 characters");
-
- RuleFor(x => x.ReceivedDate)
- .NotEmpty().WithMessage("Received date is required")
- .LessThanOrEqualTo(DateTime.Now.AddDays(1)).WithMessage("Received date cannot be in the future");
-
- RuleFor(x => x.Attachments)
- .NotNull().WithMessage("Attachments collection cannot be null");
-
- RuleForEach(x => x.Attachments).ChildRules(attachment =>
- {
- attachment.RuleFor(a => a.FileName)
- .NotEmpty().WithMessage("Attachment file name is required")
- .MaximumLength(500).WithMessage("Attachment file name must not exceed 500 characters");
-
- attachment.RuleFor(a => a.Content)
- .NotEmpty().WithMessage("Attachment content is required");
-
- attachment.RuleFor(a => a.SizeBytes)
- .GreaterThan(0).WithMessage("Attachment size must be greater than 0")
- .LessThanOrEqualTo(100 * 1024 * 1024).WithMessage("Attachment size must not exceed 100 MB");
- });
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Commands/CreateEmailProfileCommand.cs b/src/DigitalData.EmailProfiler.Application/EmailProfiles/Commands/CreateEmailProfileCommand.cs
deleted file mode 100644
index ddbdd68..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Commands/CreateEmailProfileCommand.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using DigitalData.EmailProfiler.Application.Interfaces.Repositories;
-using DigitalData.EmailProfiler.Domain.Entities;
-using MediatR;
-
-namespace DigitalData.EmailProfiler.Application.EmailProfiles.Commands;
-
-///
-/// Command to create a new email profile.
-///
-public record CreateEmailProfileCommand : IRequest
-{
- public string ProfileName { get; init; } = string.Empty;
- public int EmailAccountId { get; init; }
- public int? ProcessId { get; init; }
- public string? ValidationSql { get; init; }
- public int PollIntervalMinutes { get; init; } = 15;
- public bool IsActive { get; init; } = true;
-}
-
-public class CreateEmailProfileCommandHandler(IRepository repository)
- : IRequestHandler
-{
- public async Task Handle(CreateEmailProfileCommand request, CancellationToken cancellationToken)
- {
- var profile = await repository.CreateAsync(request, cancellationToken);
- return profile.Id;
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Commands/DeleteEmailProfileCommand.cs b/src/DigitalData.EmailProfiler.Application/EmailProfiles/Commands/DeleteEmailProfileCommand.cs
deleted file mode 100644
index 8c54b33..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Commands/DeleteEmailProfileCommand.cs
+++ /dev/null
@@ -1,20 +0,0 @@
-using DigitalData.EmailProfiler.Application.Interfaces.Repositories;
-using DigitalData.EmailProfiler.Domain.Entities;
-using MediatR;
-
-namespace DigitalData.EmailProfiler.Application.EmailProfiles.Commands;
-
-///
-/// Command to delete an email profile.
-///
-public record DeleteEmailProfileCommand(int Id) : IRequest;
-
-public class DeleteEmailProfileCommandHandler(IRepository repository)
- : IRequestHandler
-{
- public async Task Handle(DeleteEmailProfileCommand request, CancellationToken cancellationToken)
- {
- await repository.DeleteSingleAsync(p => p.Id == request.Id, cancellationToken);
- return request.Id;
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Commands/UpdateEmailProfileCommand.cs b/src/DigitalData.EmailProfiler.Application/EmailProfiles/Commands/UpdateEmailProfileCommand.cs
deleted file mode 100644
index 8ccd07e..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Commands/UpdateEmailProfileCommand.cs
+++ /dev/null
@@ -1,27 +0,0 @@
-using DigitalData.EmailProfiler.Application.Interfaces.Repositories;
-using DigitalData.EmailProfiler.Domain.Entities;
-using MediatR;
-
-namespace DigitalData.EmailProfiler.Application.EmailProfiles.Commands;
-
-///
-/// Command to update an existing email profile.
-///
-public record UpdateEmailProfileCommand : IRequest
-{
- public int Id { get; init; }
- public string ProfileName { get; init; } = string.Empty;
- public string? ValidationSql { get; init; }
- public int PollIntervalMinutes { get; init; }
- public bool IsActive { get; init; }
-}
-
-public class UpdateEmailProfileCommandHandler(IRepository repository)
- : IRequestHandler
-{
- public async Task Handle(UpdateEmailProfileCommand request, CancellationToken cancellationToken)
- {
- await repository.UpdateSingleAsync(p => p.Id == request.Id, request, cancellationToken);
- return request.Id;
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Queries/GetActiveEmailProfilesQuery.cs b/src/DigitalData.EmailProfiler.Application/EmailProfiles/Queries/GetActiveEmailProfilesQuery.cs
deleted file mode 100644
index 1945859..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Queries/GetActiveEmailProfilesQuery.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using AutoMapper;
-using DigitalData.EmailProfiler.Application.Common.Dtos;
-using DigitalData.EmailProfiler.Application.Interfaces.Repositories;
-using MediatR;
-
-namespace DigitalData.EmailProfiler.Application.EmailProfiles.Queries;
-
-///
-/// Query to get all active email profiles.
-///
-public record GetActiveEmailProfilesQuery : IRequest>;
-
-public class GetActiveEmailProfilesQueryHandler(
- IEmailProfileRepository repository,
- IMapper mapper)
- : IRequestHandler>
-{
- public async Task> Handle(
- GetActiveEmailProfilesQuery request,
- CancellationToken cancellationToken)
- {
- var profiles = await repository.GetActiveProfilesAsync(cancellationToken);
- return mapper.Map>(profiles);
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Queries/GetEmailProfileByIdQuery.cs b/src/DigitalData.EmailProfiler.Application/EmailProfiles/Queries/GetEmailProfileByIdQuery.cs
deleted file mode 100644
index 3238162..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Queries/GetEmailProfileByIdQuery.cs
+++ /dev/null
@@ -1,25 +0,0 @@
-using AutoMapper;
-using DigitalData.EmailProfiler.Application.Common.Dtos;
-using DigitalData.EmailProfiler.Application.Interfaces.Repositories;
-using MediatR;
-
-namespace DigitalData.EmailProfiler.Application.EmailProfiles.Queries;
-
-///
-/// Query to get email profile by ID with related entities.
-///
-public record GetEmailProfileByIdQuery(int Id) : IRequest;
-
-public class GetEmailProfileByIdQueryHandler(
- IEmailProfileRepository repository,
- IMapper mapper)
- : IRequestHandler
-{
- public async Task Handle(
- GetEmailProfileByIdQuery request,
- CancellationToken cancellationToken)
- {
- var profile = await repository.GetWithRelatedEntitiesAsync(request.Id, cancellationToken);
- return profile != null ? mapper.Map(profile) : null;
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Queries/GetEmailProfilesQuery.cs b/src/DigitalData.EmailProfiler.Application/EmailProfiles/Queries/GetEmailProfilesQuery.cs
deleted file mode 100644
index f719426..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Queries/GetEmailProfilesQuery.cs
+++ /dev/null
@@ -1,26 +0,0 @@
-using AutoMapper;
-using DigitalData.EmailProfiler.Application.Common.Dtos;
-using DigitalData.EmailProfiler.Application.Interfaces.Repositories;
-using DigitalData.EmailProfiler.Domain.Entities;
-using MediatR;
-
-namespace DigitalData.EmailProfiler.Application.EmailProfiles.Queries;
-
-///
-/// Query to get all email profiles.
-///
-public record GetEmailProfilesQuery : IRequest>;
-
-public class GetEmailProfilesQueryHandler(
- IRepository repository,
- IMapper mapper)
- : IRequestHandler>
-{
- public async Task> Handle(
- GetEmailProfilesQuery request,
- CancellationToken cancellationToken)
- {
- var profiles = await repository.GetAllAsync(cancellationToken);
- return mapper.Map>(profiles);
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Validators/CreateEmailProfileCommandValidator.cs b/src/DigitalData.EmailProfiler.Application/EmailProfiles/Validators/CreateEmailProfileCommandValidator.cs
deleted file mode 100644
index 468f31d..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Validators/CreateEmailProfileCommandValidator.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using DigitalData.EmailProfiler.Application.EmailProfiles.Commands;
-using FluentValidation;
-
-namespace DigitalData.EmailProfiler.Application.EmailProfiles.Validators;
-
-///
-/// Validator for CreateEmailProfileCommand.
-///
-public class CreateEmailProfileCommandValidator : AbstractValidator
-{
- public CreateEmailProfileCommandValidator()
- {
- RuleFor(x => x.ProfileName)
- .NotEmpty().WithMessage("Profile name is required")
- .MaximumLength(100).WithMessage("Profile name must not exceed 100 characters");
-
- RuleFor(x => x.EmailAccountId)
- .GreaterThan(0).WithMessage("Email account ID must be greater than 0");
-
- RuleFor(x => x.PollIntervalMinutes)
- .GreaterThanOrEqualTo(1).WithMessage("Poll interval must be at least 1 minute")
- .LessThanOrEqualTo(1440).WithMessage("Poll interval must not exceed 1440 minutes (24 hours)");
-
- RuleFor(x => x.ValidationSql)
- .MaximumLength(1000).WithMessage("Validation SQL must not exceed 1000 characters")
- .When(x => !string.IsNullOrEmpty(x.ValidationSql));
- }
-}
diff --git a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Validators/UpdateEmailProfileCommandValidator.cs b/src/DigitalData.EmailProfiler.Application/EmailProfiles/Validators/UpdateEmailProfileCommandValidator.cs
deleted file mode 100644
index 222168b..0000000
--- a/src/DigitalData.EmailProfiler.Application/EmailProfiles/Validators/UpdateEmailProfileCommandValidator.cs
+++ /dev/null
@@ -1,28 +0,0 @@
-using DigitalData.EmailProfiler.Application.EmailProfiles.Commands;
-using FluentValidation;
-
-namespace DigitalData.EmailProfiler.Application.EmailProfiles.Validators;
-
-///
-/// Validator for UpdateEmailProfileCommand.
-///
-public class UpdateEmailProfileCommandValidator : AbstractValidator
-{
- public UpdateEmailProfileCommandValidator()
- {
- RuleFor(x => x.Id)
- .GreaterThan(0).WithMessage("Profile ID must be greater than 0");
-
- RuleFor(x => x.ProfileName)
- .NotEmpty().WithMessage("Profile name is required")
- .MaximumLength(100).WithMessage("Profile name must not exceed 100 characters");
-
- RuleFor(x => x.PollIntervalMinutes)
- .GreaterThanOrEqualTo(1).WithMessage("Poll interval must be at least 1 minute")
- .LessThanOrEqualTo(1440).WithMessage("Poll interval must not exceed 1440 minutes (24 hours)");
-
- RuleFor(x => x.ValidationSql)
- .MaximumLength(1000).WithMessage("Validation SQL must not exceed 1000 characters")
- .When(x => !string.IsNullOrEmpty(x.ValidationSql));
- }
-}