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)); - } -}