diff --git a/agents.md b/AGENTS.md similarity index 92% rename from agents.md rename to AGENTS.md index 4464866..5218a8f 100644 --- a/agents.md +++ b/AGENTS.md @@ -55,8 +55,32 @@ var lastPoll = DateTime.UtcNow.AddMinutes(-profile.PollIntervalMinutes); // NEV - All timestamps in logs and error messages - All date parameters in queries -### 4. No Commits Without Permission -**NEVER** commit changes to git automatically. Always wait for explicit user instruction to commit. +### 4. Git Operations - NEVER Without Explicit Permission +**CRITICAL**: NEVER execute `git commit` or `git push` commands automatically. ALWAYS wait for explicit user instruction. + +**Rules**: +- Only commit when user explicitly says "commit" or "commit this" +- Only push when user explicitly says "push" or "push to remote" +- Stage files with `git add` ONLY when about to commit per user request + +### 5. MediatR Command/Query File Organization +**IMPORTANT**: Commands/Queries and their Handlers must be in the SAME file. + +**Example**: +```csharp +// ✅ CORRECT - CreateEmailProfileCommand.cs contains BOTH +public record CreateEmailProfileCommand : IRequest { ... } + +public class CreateEmailProfileCommandHandler : IRequestHandler { ... } + +// ❌ WRONG - Separate files +// CreateEmailProfileCommand.cs (command only) +// CreateEmailProfileCommandHandler.cs (handler only) +``` + +**File Naming**: +- Commands: `{Verb}{Entity}Command.cs` (e.g., `CreateEmailProfileCommand.cs`) +- Queries: `{Verb}{Entity}Query.cs` (e.g., `GetEmailProfilesQuery.cs`) --- diff --git a/DigitalData.EmailProfiler.sln b/DigitalData.EmailProfiler.sln index e198862..304c4c8 100644 --- a/DigitalData.EmailProfiler.sln +++ b/DigitalData.EmailProfiler.sln @@ -25,6 +25,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution STATUS.md = STATUS.md EndProjectSection EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EmailProfiler.Common", "legacy\App\EmailProfiler.Common\EmailProfiler.Common.vbproj", "{9F748DCD-952E-40A0-9DAD-65BF8A39B231}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "legacy", "legacy", "{EAFC1552-2C62-4C00-AE27-47D76FEAE9F5}" +EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "EmailProfiler.Service", "legacy\App\EmailProfiler.Service\EmailProfiler.Service.vbproj", "{1F3C569B-91DA-427F-8D81-BBCC556B11A4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -51,6 +57,14 @@ Global {211FB65F-2406-474E-A426-DA246B250AB8}.Debug|Any CPU.Build.0 = Debug|Any CPU {211FB65F-2406-474E-A426-DA246B250AB8}.Release|Any CPU.ActiveCfg = Release|Any CPU {211FB65F-2406-474E-A426-DA246B250AB8}.Release|Any CPU.Build.0 = Release|Any CPU + {9F748DCD-952E-40A0-9DAD-65BF8A39B231}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9F748DCD-952E-40A0-9DAD-65BF8A39B231}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9F748DCD-952E-40A0-9DAD-65BF8A39B231}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9F748DCD-952E-40A0-9DAD-65BF8A39B231}.Release|Any CPU.Build.0 = Release|Any CPU + {1F3C569B-91DA-427F-8D81-BBCC556B11A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F3C569B-91DA-427F-8D81-BBCC556B11A4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F3C569B-91DA-427F-8D81-BBCC556B11A4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F3C569B-91DA-427F-8D81-BBCC556B11A4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -61,6 +75,8 @@ Global {76ADC1D0-4DFA-0B1E-57C9-2636434A0043} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {1874A827-C6A5-EB5E-0FE9-30A7200382B7} = {02EA681E-C7D8-13C7-8484-4AC65E1B71E8} {211FB65F-2406-474E-A426-DA246B250AB8} = {4F20FEFD-9289-42C6-ABA6-8DB236D74559} + {9F748DCD-952E-40A0-9DAD-65BF8A39B231} = {EAFC1552-2C62-4C00-AE27-47D76FEAE9F5} + {1F3C569B-91DA-427F-8D81-BBCC556B11A4} = {EAFC1552-2C62-4C00-AE27-47D76FEAE9F5} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {90E29FDC-F6C6-414F-94BF-25DF61D18060} diff --git a/src/DigitalData.EmailProfiler.Application/Features/EmailAccounts/Commands/CreateEmailAccountCommand.cs b/src/DigitalData.EmailProfiler.Application/Features/EmailAccounts/Commands/CreateEmailAccountCommand.cs index ea1c808..3992c4c 100644 --- a/src/DigitalData.EmailProfiler.Application/Features/EmailAccounts/Commands/CreateEmailAccountCommand.cs +++ b/src/DigitalData.EmailProfiler.Application/Features/EmailAccounts/Commands/CreateEmailAccountCommand.cs @@ -1,3 +1,5 @@ +using DigitalData.EmailProfiler.Application.Interfaces.Repositories; +using DigitalData.EmailProfiler.Domain.Entities; using MediatR; namespace DigitalData.EmailProfiler.Application.Features.EmailAccounts.Commands; @@ -22,3 +24,35 @@ public record CreateEmailAccountCommand : IRequest public string? EncryptedClientSecret { get; init; } // Already encrypted by client public bool IsActive { get; init; } = true; } + +public class CreateEmailAccountCommandHandler(IUnitOfWork unitOfWork) + : IRequestHandler +{ + public async Task Handle(CreateEmailAccountCommand request, CancellationToken cancellationToken) + { + var account = new EmailAccount + { + AccountName = request.AccountName, + Username = request.Username, + ImapServer = request.ImapServer, + ImapPort = request.ImapPort, + ImapUseSsl = request.ImapUseSsl, + SmtpServer = request.SmtpServer, + SmtpPort = request.SmtpPort, + SmtpUseSsl = request.SmtpUseSsl, + UseOAuth2 = request.UseOAuth2, + EncryptedPassword = request.EncryptedPassword, + TenantId = request.TenantId, + ClientId = request.ClientId, + EncryptedClientSecret = request.EncryptedClientSecret, + IsActive = request.IsActive, + AddedWhen = DateTime.Now, + AddedWho = "System" // TODO: Get from current user context + }; + + var createdAccount = await unitOfWork.EmailAccounts.AddAsync(account, cancellationToken); + await unitOfWork.SaveChangesAsync(cancellationToken); + + return createdAccount.Id; + } +} diff --git a/src/DigitalData.EmailProfiler.Application/Features/EmailAccounts/Commands/CreateEmailAccountCommandHandler.cs b/src/DigitalData.EmailProfiler.Application/Features/EmailAccounts/Commands/CreateEmailAccountCommandHandler.cs deleted file mode 100644 index 016c721..0000000 --- a/src/DigitalData.EmailProfiler.Application/Features/EmailAccounts/Commands/CreateEmailAccountCommandHandler.cs +++ /dev/null @@ -1,37 +0,0 @@ -using DigitalData.EmailProfiler.Application.Interfaces.Repositories; -using DigitalData.EmailProfiler.Domain.Entities; -using MediatR; - -namespace DigitalData.EmailProfiler.Application.Features.EmailAccounts.Commands; - -public class CreateEmailAccountCommandHandler(IUnitOfWork unitOfWork) - : IRequestHandler -{ - public async Task Handle(CreateEmailAccountCommand request, CancellationToken cancellationToken) - { - var account = new EmailAccount - { - AccountName = request.AccountName, - Username = request.Username, - ImapServer = request.ImapServer, - ImapPort = request.ImapPort, - ImapUseSsl = request.ImapUseSsl, - SmtpServer = request.SmtpServer, - SmtpPort = request.SmtpPort, - SmtpUseSsl = request.SmtpUseSsl, - UseOAuth2 = request.UseOAuth2, - EncryptedPassword = request.EncryptedPassword, - TenantId = request.TenantId, - ClientId = request.ClientId, - EncryptedClientSecret = request.EncryptedClientSecret, - IsActive = request.IsActive, - AddedWhen = DateTime.Now, - AddedWho = "System" // TODO: Get from current user context - }; - - var createdAccount = await unitOfWork.EmailAccounts.AddAsync(account, cancellationToken); - await unitOfWork.SaveChangesAsync(cancellationToken); - - return createdAccount.Id; - } -} diff --git a/src/DigitalData.EmailProfiler.Application/Features/EmailProcessing/Commands/ProcessEmailCommand.cs b/src/DigitalData.EmailProfiler.Application/Features/EmailProcessing/Commands/ProcessEmailCommand.cs index e38e1a4..99c61b5 100644 --- a/src/DigitalData.EmailProfiler.Application/Features/EmailProcessing/Commands/ProcessEmailCommand.cs +++ b/src/DigitalData.EmailProfiler.Application/Features/EmailProcessing/Commands/ProcessEmailCommand.cs @@ -1,3 +1,10 @@ +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.Features.EmailProcessing.Commands; @@ -26,3 +33,120 @@ public record AttachmentData( byte[] Content, string ContentType, long SizeBytes); + +public class ProcessEmailCommandHandler( + IUnitOfWork unitOfWork, + 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 unitOfWork.EmailProfiles.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 unitOfWork.EmailHistories.IsDuplicateAsync(messageId.Hash, cancellationToken); + if (isDuplicate) + { + throw new ValidationException("Email already processed (duplicate detected)", ErrorCode.DuplicateMessageId); + } + + // 4. Create email history record + var emailHistory = new EmailHistory + { + ProfileId = profile.Id, + MessageIdHash = messageId.Hash, + OriginalMessageId = request.MessageId, + SenderAddress = request.Sender, + EmailDate = request.ReceivedDate, + Subject = request.Subject, + EmailBodyText = request.BodyText, + EmailBodyHtml = request.BodyHtml, + Status = EmailStatus.Processing.ToString(), + AddedWhen = DateTime.Now + }; + + var createdHistory = await unitOfWork.EmailHistories.AddAsync(emailHistory, cancellationToken); + await unitOfWork.SaveChangesAsync(cancellationToken); + + try + { + // 5. Process attachments + foreach (var attachmentData in request.Attachments) + { + var attachment = new EmailAttachment + { + 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), + Status = AttachmentStatus.Pending.ToString(), + AddedWhen = DateTime.Now + }; + + // Validate PDF attachments + if (attachmentData.ContentType.Contains("pdf", StringComparison.OrdinalIgnoreCase)) + { + using var stream = new MemoryStream(attachmentData.Content); + var isValidPdf = await pdfService.IsValidPdfAsync(stream, cancellationToken); + + if (isValidPdf) + { + attachment.MarkAsValid(); + } + else + { + attachment.MarkAsCorrupt(ErrorCode.PdfStructureInvalid, "Invalid PDF structure"); + } + } + + createdHistory.Attachments.Add(attachment); + } + + // 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 + createdHistory.MarkAsProcessed(); + await unitOfWork.EmailHistories.UpdateAsync(createdHistory, cancellationToken); + await unitOfWork.SaveChangesAsync(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 + createdHistory.MarkAsFailed(ErrorCode.AttachmentExtractionFailed, ex.Message); + await unitOfWork.EmailHistories.UpdateAsync(createdHistory, cancellationToken); + await unitOfWork.SaveChangesAsync(cancellationToken); + + throw; + } + } +} diff --git a/src/DigitalData.EmailProfiler.Application/Features/EmailProcessing/Commands/ProcessEmailCommandHandler.cs b/src/DigitalData.EmailProfiler.Application/Features/EmailProcessing/Commands/ProcessEmailCommandHandler.cs deleted file mode 100644 index 28e0f8c..0000000 --- a/src/DigitalData.EmailProfiler.Application/Features/EmailProcessing/Commands/ProcessEmailCommandHandler.cs +++ /dev/null @@ -1,127 +0,0 @@ -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.Features.EmailProcessing.Commands; - -public class ProcessEmailCommandHandler( - IUnitOfWork unitOfWork, - 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 unitOfWork.EmailProfiles.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 unitOfWork.EmailHistories.IsDuplicateAsync(messageId.Hash, cancellationToken); - if (isDuplicate) - { - throw new ValidationException("Email already processed (duplicate detected)", ErrorCode.DuplicateMessageId); - } - - // 4. Create email history record - var emailHistory = new EmailHistory - { - ProfileId = profile.Id, - MessageIdHash = messageId.Hash, - OriginalMessageId = request.MessageId, - SenderAddress = request.Sender, - EmailDate = request.ReceivedDate, - Subject = request.Subject, - EmailBodyText = request.BodyText, - EmailBodyHtml = request.BodyHtml, - Status = EmailStatus.Processing.ToString(), - AddedWhen = DateTime.Now - }; - - var createdHistory = await unitOfWork.EmailHistories.AddAsync(emailHistory, cancellationToken); - await unitOfWork.SaveChangesAsync(cancellationToken); - - try - { - // 5. Process attachments - foreach (var attachmentData in request.Attachments) - { - var attachment = new EmailAttachment - { - 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), - Status = AttachmentStatus.Pending.ToString(), - AddedWhen = DateTime.Now - }; - - // Validate PDF attachments - if (attachmentData.ContentType.Contains("pdf", StringComparison.OrdinalIgnoreCase)) - { - using var stream = new MemoryStream(attachmentData.Content); - var isValidPdf = await pdfService.IsValidPdfAsync(stream, cancellationToken); - - if (isValidPdf) - { - attachment.MarkAsValid(); - } - else - { - attachment.MarkAsCorrupt(ErrorCode.PdfStructureInvalid, "Invalid PDF structure"); - } - } - - createdHistory.Attachments.Add(attachment); - } - - // 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 - createdHistory.MarkAsProcessed(); - await unitOfWork.EmailHistories.UpdateAsync(createdHistory, cancellationToken); - await unitOfWork.SaveChangesAsync(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 - createdHistory.MarkAsFailed(ErrorCode.AttachmentExtractionFailed, ex.Message); - await unitOfWork.EmailHistories.UpdateAsync(createdHistory, cancellationToken); - await unitOfWork.SaveChangesAsync(cancellationToken); - - throw; - } - } -} diff --git a/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/CreateEmailProfileCommand.cs b/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/CreateEmailProfileCommand.cs index 8b5473a..8db9b88 100644 --- a/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/CreateEmailProfileCommand.cs +++ b/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/CreateEmailProfileCommand.cs @@ -1,3 +1,5 @@ +using DigitalData.EmailProfiler.Application.Interfaces.Repositories; +using DigitalData.EmailProfiler.Domain.Entities; using MediatR; namespace DigitalData.EmailProfiler.Application.Features.EmailProfiles.Commands; @@ -14,3 +16,27 @@ public record CreateEmailProfileCommand : IRequest public int PollIntervalMinutes { get; init; } = 15; public bool IsActive { get; init; } = true; } + +public class CreateEmailProfileCommandHandler(IUnitOfWork unitOfWork) + : IRequestHandler +{ + public async Task Handle(CreateEmailProfileCommand request, CancellationToken cancellationToken) + { + var profile = new EmailProfile + { + ProfileName = request.ProfileName, + EmailAccountId = request.EmailAccountId, + ProcessId = request.ProcessId, + ValidationSql = request.ValidationSql, + PollIntervalMinutes = request.PollIntervalMinutes, + IsActive = request.IsActive, + AddedWhen = DateTime.Now, + AddedWho = "System" // TODO: Get from current user context + }; + + var createdProfile = await unitOfWork.EmailProfiles.AddAsync(profile, cancellationToken); + await unitOfWork.SaveChangesAsync(cancellationToken); + + return createdProfile.Id; + } +} diff --git a/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/CreateEmailProfileCommandHandler.cs b/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/CreateEmailProfileCommandHandler.cs deleted file mode 100644 index 0773c3b..0000000 --- a/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/CreateEmailProfileCommandHandler.cs +++ /dev/null @@ -1,29 +0,0 @@ -using DigitalData.EmailProfiler.Application.Interfaces.Repositories; -using DigitalData.EmailProfiler.Domain.Entities; -using MediatR; - -namespace DigitalData.EmailProfiler.Application.Features.EmailProfiles.Commands; - -public class CreateEmailProfileCommandHandler(IUnitOfWork unitOfWork) - : IRequestHandler -{ - public async Task Handle(CreateEmailProfileCommand request, CancellationToken cancellationToken) - { - var profile = new EmailProfile - { - ProfileName = request.ProfileName, - EmailAccountId = request.EmailAccountId, - ProcessId = request.ProcessId, - ValidationSql = request.ValidationSql, - PollIntervalMinutes = request.PollIntervalMinutes, - IsActive = request.IsActive, - AddedWhen = DateTime.Now, - AddedWho = "System" // TODO: Get from current user context - }; - - var createdProfile = await unitOfWork.EmailProfiles.AddAsync(profile, cancellationToken); - await unitOfWork.SaveChangesAsync(cancellationToken); - - return createdProfile.Id; - } -} diff --git a/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/DeleteEmailProfileCommand.cs b/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/DeleteEmailProfileCommand.cs index f4ec819..c214c89 100644 --- a/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/DeleteEmailProfileCommand.cs +++ b/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/DeleteEmailProfileCommand.cs @@ -1,3 +1,5 @@ +using DigitalData.EmailProfiler.Application.Interfaces.Repositories; +using DigitalData.EmailProfiler.Domain.Exceptions; using MediatR; namespace DigitalData.EmailProfiler.Application.Features.EmailProfiles.Commands; @@ -6,3 +8,18 @@ namespace DigitalData.EmailProfiler.Application.Features.EmailProfiles.Commands; /// Command to delete an email profile. /// public record DeleteEmailProfileCommand(int Id) : IRequest; + +public class DeleteEmailProfileCommandHandler(IUnitOfWork unitOfWork) + : IRequestHandler +{ + public async Task Handle(DeleteEmailProfileCommand request, CancellationToken cancellationToken) + { + var profile = await unitOfWork.EmailProfiles.GetByIdAsync(request.Id, cancellationToken) + ?? throw new DomainException($"Email profile with ID {request.Id} not found"); + + await unitOfWork.EmailProfiles.DeleteAsync(profile, cancellationToken); + await unitOfWork.SaveChangesAsync(cancellationToken); + + return Unit.Value; + } +} diff --git a/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/DeleteEmailProfileCommandHandler.cs b/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/DeleteEmailProfileCommandHandler.cs deleted file mode 100644 index b3d1e2c..0000000 --- a/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/DeleteEmailProfileCommandHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using DigitalData.EmailProfiler.Application.Interfaces.Repositories; -using DigitalData.EmailProfiler.Domain.Exceptions; -using MediatR; - -namespace DigitalData.EmailProfiler.Application.Features.EmailProfiles.Commands; - -public class DeleteEmailProfileCommandHandler(IUnitOfWork unitOfWork) - : IRequestHandler -{ - public async Task Handle(DeleteEmailProfileCommand request, CancellationToken cancellationToken) - { - var profile = await unitOfWork.EmailProfiles.GetByIdAsync(request.Id, cancellationToken) - ?? throw new DomainException($"Email profile with ID {request.Id} not found"); - - await unitOfWork.EmailProfiles.DeleteAsync(profile, cancellationToken); - await unitOfWork.SaveChangesAsync(cancellationToken); - - return Unit.Value; - } -} diff --git a/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/UpdateEmailProfileCommand.cs b/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/UpdateEmailProfileCommand.cs index 1dd9363..da6c6d3 100644 --- a/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/UpdateEmailProfileCommand.cs +++ b/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/UpdateEmailProfileCommand.cs @@ -1,3 +1,5 @@ +using DigitalData.EmailProfiler.Application.Interfaces.Repositories; +using DigitalData.EmailProfiler.Domain.Exceptions; using MediatR; namespace DigitalData.EmailProfiler.Application.Features.EmailProfiles.Commands; @@ -13,3 +15,25 @@ public record UpdateEmailProfileCommand : IRequest public int PollIntervalMinutes { get; init; } public bool IsActive { get; init; } } + +public class UpdateEmailProfileCommandHandler(IUnitOfWork unitOfWork) + : IRequestHandler +{ + public async Task Handle(UpdateEmailProfileCommand request, CancellationToken cancellationToken) + { + var profile = await unitOfWork.EmailProfiles.GetByIdAsync(request.Id, cancellationToken) + ?? throw new DomainException($"Email profile with ID {request.Id} not found"); + + profile.ProfileName = request.ProfileName; + profile.ValidationSql = request.ValidationSql; + profile.PollIntervalMinutes = request.PollIntervalMinutes; + profile.IsActive = request.IsActive; + profile.ChangedWhen = DateTime.Now; + profile.ChangedWho = "System"; // TODO: Get from current user context + + await unitOfWork.EmailProfiles.UpdateAsync(profile, cancellationToken); + await unitOfWork.SaveChangesAsync(cancellationToken); + + return Unit.Value; + } +} diff --git a/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/UpdateEmailProfileCommandHandler.cs b/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/UpdateEmailProfileCommandHandler.cs deleted file mode 100644 index 52663d3..0000000 --- a/src/DigitalData.EmailProfiler.Application/Features/EmailProfiles/Commands/UpdateEmailProfileCommandHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using DigitalData.EmailProfiler.Application.Interfaces.Repositories; -using DigitalData.EmailProfiler.Domain.Exceptions; -using MediatR; - -namespace DigitalData.EmailProfiler.Application.Features.EmailProfiles.Commands; - -public class UpdateEmailProfileCommandHandler(IUnitOfWork unitOfWork) - : IRequestHandler -{ - public async Task Handle(UpdateEmailProfileCommand request, CancellationToken cancellationToken) - { - var profile = await unitOfWork.EmailProfiles.GetByIdAsync(request.Id, cancellationToken) - ?? throw new DomainException($"Email profile with ID {request.Id} not found"); - - profile.ProfileName = request.ProfileName; - profile.ValidationSql = request.ValidationSql; - profile.PollIntervalMinutes = request.PollIntervalMinutes; - profile.IsActive = request.IsActive; - profile.ChangedWhen = DateTime.Now; - profile.ChangedWho = "System"; // TODO: Get from current user context - - await unitOfWork.EmailProfiles.UpdateAsync(profile, cancellationToken); - await unitOfWork.SaveChangesAsync(cancellationToken); - - return Unit.Value; - } -}