refactor(application): Remove old repository and service interfaces

This commit is contained in:
2026-07-23 11:19:41 +02:00
parent 860ce41192
commit 70dd210555
11 changed files with 0 additions and 462 deletions

View File

@@ -1,24 +0,0 @@
using DigitalData.EmailProfiler.Domain.Entities;
namespace DigitalData.EmailProfiler.Application.Interfaces.Repositories;
/// <summary>
/// Repository interface for EmailAccount entity.
/// </summary>
public interface IEmailAccountRepository : IRepository<EmailAccount>
{
/// <summary>
/// Get all active email accounts.
/// </summary>
Task<IEnumerable<EmailAccount>> GetActiveAccountsAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Get email account by account name.
/// </summary>
Task<EmailAccount?> GetByAccountNameAsync(string accountName, CancellationToken cancellationToken = default);
/// <summary>
/// Get email account with its profiles.
/// </summary>
Task<EmailAccount?> GetWithProfilesAsync(int id, CancellationToken cancellationToken = default);
}

View File

@@ -1,47 +0,0 @@
using DigitalData.EmailProfiler.Domain.Entities;
namespace DigitalData.EmailProfiler.Application.Interfaces.Repositories;
/// <summary>
/// Repository interface for EmailHistory entity.
/// </summary>
public interface IEmailHistoryRepository : IRepository<EmailHistory>
{
/// <summary>
/// Get email history by message ID hash.
/// </summary>
Task<EmailHistory?> GetByMessageIdHashAsync(string messageIdHash, CancellationToken cancellationToken = default);
/// <summary>
/// Get email history with attachments.
/// </summary>
Task<EmailHistory?> GetWithAttachmentsAsync(int id, CancellationToken cancellationToken = default);
/// <summary>
/// Get email history by profile ID with pagination.
/// </summary>
Task<(IEnumerable<EmailHistory> Items, int TotalCount)> GetByProfileIdAsync(
int profileId,
int pageNumber = 1,
int pageSize = 50,
CancellationToken cancellationToken = default);
/// <summary>
/// Get failed emails that need retry.
/// </summary>
Task<IEnumerable<EmailHistory>> GetFailedEmailsAsync(int? profileId = null, CancellationToken cancellationToken = default);
/// <summary>
/// Get emails processed within date range.
/// </summary>
Task<IEnumerable<EmailHistory>> GetByDateRangeAsync(
DateTime startDate,
DateTime endDate,
int? profileId = null,
CancellationToken cancellationToken = default);
/// <summary>
/// Check if email with given message ID hash already exists.
/// </summary>
Task<bool> IsDuplicateAsync(string messageIdHash, CancellationToken cancellationToken = default);
}

View File

@@ -1,34 +0,0 @@
using DigitalData.EmailProfiler.Domain.Entities;
namespace DigitalData.EmailProfiler.Application.Interfaces.Repositories;
/// <summary>
/// Repository interface for EmailOutbox entity.
/// </summary>
public interface IEmailOutboxRepository : IRepository<EmailOutbox>
{
/// <summary>
/// Get all pending emails (not yet sent).
/// </summary>
Task<IEnumerable<EmailOutbox>> GetPendingEmailsAsync(int maxCount = 100, CancellationToken cancellationToken = default);
/// <summary>
/// Get failed emails that need retry (RetryCount < MaxRetryCount).
/// </summary>
Task<IEnumerable<EmailOutbox>> GetFailedEmailsForRetryAsync(int maxCount = 100, CancellationToken cancellationToken = default);
/// <summary>
/// Mark email as sent.
/// </summary>
Task MarkAsSentAsync(int id, CancellationToken cancellationToken = default);
/// <summary>
/// Mark email as failed with error details.
/// </summary>
Task MarkAsFailedAsync(int id, string errorMessage, CancellationToken cancellationToken = default);
/// <summary>
/// Delete old sent emails (cleanup).
/// </summary>
Task DeleteOldSentEmailsAsync(int daysToKeep, CancellationToken cancellationToken = default);
}

View File

@@ -1,24 +0,0 @@
using DigitalData.EmailProfiler.Domain.Entities;
namespace DigitalData.EmailProfiler.Application.Interfaces.Repositories;
/// <summary>
/// Repository interface for EmailProcess entity.
/// </summary>
public interface IEmailProcessRepository : IRepository<EmailProcess>
{
/// <summary>
/// Get process with all steps (ProcessSteps and IndexingSteps).
/// </summary>
Task<EmailProcess?> GetWithStepsAsync(int id, CancellationToken cancellationToken = default);
/// <summary>
/// Get process by profile ID.
/// </summary>
Task<EmailProcess?> GetByProfileIdAsync(int profileId, CancellationToken cancellationToken = default);
/// <summary>
/// Get all processes by type.
/// </summary>
Task<IEnumerable<EmailProcess>> GetByProcessTypeAsync(string processType, CancellationToken cancellationToken = default);
}

View File

@@ -1,29 +0,0 @@
using DigitalData.EmailProfiler.Domain.Entities;
namespace DigitalData.EmailProfiler.Application.Interfaces.Repositories;
/// <summary>
/// Repository interface for EmailProfile entity.
/// </summary>
public interface IEmailProfileRepository : IRepository<EmailProfile>
{
/// <summary>
/// Get all active profiles.
/// </summary>
Task<IEnumerable<EmailProfile>> GetActiveProfilesAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Get profiles that should be polled now (based on PollIntervalMinutes).
/// </summary>
Task<IEnumerable<EmailProfile>> GetProfilesDueForPollingAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Get profile with all related entities (EmailAccount, EmailProcess, ProcessSteps).
/// </summary>
Task<EmailProfile?> GetWithRelatedEntitiesAsync(int id, CancellationToken cancellationToken = default);
/// <summary>
/// Get profiles by email account ID.
/// </summary>
Task<IEnumerable<EmailProfile>> GetByEmailAccountIdAsync(int emailAccountId, CancellationToken cancellationToken = default);
}

View File

@@ -1,60 +0,0 @@
using System.Linq.Expressions;
namespace DigitalData.EmailProfiler.Application.Interfaces.Repositories;
/// <summary>
/// Base repository interface for common CRUD operations with AutoMapper support.
/// Changes are automatically saved after each operation - no explicit SaveChanges needed.
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
public interface IRepository<TEntity> where TEntity : class
{
// ==================== QUERY OPERATIONS ====================
Task<TEntity?> GetByIdAsync(int id, CancellationToken cancellationToken = default);
Task<IEnumerable<TEntity>> GetAllAsync(CancellationToken cancellationToken = default);
Task<IEnumerable<TEntity>> FindAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
Task<TEntity?> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
Task<bool> ExistsAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
Task<int> CountAsync(Expression<Func<TEntity, bool>>? predicate = null, CancellationToken cancellationToken = default);
// ==================== CREATE OPERATIONS ====================
/// <summary>
/// Create entity from DTO using AutoMapper and save immediately.
/// </summary>
/// <returns>Created entity with ID populated</returns>
Task<TEntity> CreateAsync<TDto>(TDto dto, CancellationToken cancellationToken = default) where TDto : class;
// ==================== UPDATE OPERATIONS ====================
/// <summary>
/// Update ALL entities matching expression using DTO via AutoMapper.
/// WARNING: This can update multiple records. Use UpdateSingleAsync for single-record updates.
/// </summary>
/// <returns>Number of entities updated</returns>
Task<int> UpdateAsync<TDto>(Expression<Func<TEntity, bool>> predicate, TDto dto, CancellationToken cancellationToken = default) where TDto : class;
/// <summary>
/// Update SINGLE entity matching expression using DTO via AutoMapper.
/// SAFETY: Throws exception if zero or multiple entities match the predicate.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when zero or multiple entities match</exception>
Task UpdateSingleAsync<TDto>(Expression<Func<TEntity, bool>> predicate, TDto dto, CancellationToken cancellationToken = default) where TDto : class;
// ==================== DELETE OPERATIONS ====================
/// <summary>
/// Delete ALL entities matching expression.
/// WARNING: This can delete multiple records. Use DeleteSingleAsync for single-record deletes.
/// </summary>
/// <returns>Number of entities deleted</returns>
Task<int> DeleteAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
/// <summary>
/// Delete SINGLE entity matching expression.
/// SAFETY: Throws exception if zero or multiple entities match the predicate.
/// </summary>
/// <exception cref="InvalidOperationException">Thrown when zero or multiple entities match</exception>
Task DeleteSingleAsync(Expression<Func<TEntity, bool>> predicate, CancellationToken cancellationToken = default);
}

View File

@@ -1,71 +0,0 @@
namespace DigitalData.EmailProfiler.Application.Interfaces.Services;
/// <summary>
/// windream DMS integration service interface.
/// </summary>
public interface IDmsService
{
/// <summary>
/// Connect to windream DMS.
/// </summary>
Task ConnectAsync(string server, string username, string password, CancellationToken cancellationToken = default);
/// <summary>
/// Archive document to windream DMS with indexing fields.
/// </summary>
Task<string> ArchiveDocumentAsync(
string filePath,
string documentType,
Dictionary<string, object> indexFields,
CancellationToken cancellationToken = default);
/// <summary>
/// Archive document from stream to windream DMS.
/// </summary>
Task<string> ArchiveDocumentAsync(
Stream fileStream,
string fileName,
string documentType,
Dictionary<string, object> indexFields,
CancellationToken cancellationToken = default);
/// <summary>
/// Check if document already exists in windream DMS.
/// </summary>
Task<bool> DocumentExistsAsync(string documentId, CancellationToken cancellationToken = default);
/// <summary>
/// Get document from windream DMS.
/// </summary>
Task<Stream> GetDocumentAsync(string documentId, CancellationToken cancellationToken = default);
/// <summary>
/// Update document indexing fields.
/// </summary>
Task UpdateIndexFieldsAsync(
string documentId,
Dictionary<string, object> indexFields,
CancellationToken cancellationToken = default);
/// <summary>
/// Search documents by index fields.
/// </summary>
Task<IEnumerable<DmsSearchResult>> SearchDocumentsAsync(
Dictionary<string, object> searchCriteria,
int maxResults = 100,
CancellationToken cancellationToken = default);
/// <summary>
/// Disconnect from windream DMS.
/// </summary>
Task DisconnectAsync(CancellationToken cancellationToken = default);
}
/// <summary>
/// Represents a windream DMS search result.
/// </summary>
public record DmsSearchResult(
string DocumentId,
string FileName,
DateTime CreatedDate,
Dictionary<string, object> IndexFields);

View File

@@ -1,27 +0,0 @@
using DigitalData.EmailProfiler.Domain.Entities;
namespace DigitalData.EmailProfiler.Application.Interfaces.Services;
/// <summary>
/// Email queue interface for asynchronous email sending.
/// Current implementation: In-memory Channel&lt;T&gt;
/// Future: RabbitMQ (see agents.md)
/// </summary>
public interface IEmailQueue
{
/// <summary>
/// Enqueue an email for sending.
/// </summary>
Task EnqueueAsync(EmailOutbox email, CancellationToken cancellationToken = default);
/// <summary>
/// Dequeue an email for sending.
/// Returns null if queue is empty.
/// </summary>
Task<EmailOutbox?> DequeueAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Get current queue depth (number of pending emails).
/// </summary>
Task<int> GetQueueDepthAsync(CancellationToken cancellationToken = default);
}

View File

@@ -1,59 +0,0 @@
using MimeKit;
namespace DigitalData.EmailProfiler.Application.Interfaces.Services;
/// <summary>
/// Email service interface for IMAP/SMTP operations with OAuth2 support.
/// </summary>
public interface IEmailService
{
/// <summary>
/// Connect to IMAP server and authenticate.
/// </summary>
Task ConnectImapAsync(string server, int port, string username, string password, bool useSsl = true, CancellationToken cancellationToken = default);
/// <summary>
/// Connect to IMAP server using OAuth2.
/// </summary>
Task ConnectImapOAuth2Async(string server, int port, string username, string accessToken, bool useSsl = true, CancellationToken cancellationToken = default);
/// <summary>
/// Fetch unread emails from inbox.
/// </summary>
Task<IEnumerable<MimeMessage>> FetchUnreadEmailsAsync(int maxCount = 100, CancellationToken cancellationToken = default);
/// <summary>
/// Mark email as read.
/// </summary>
Task MarkAsReadAsync(int uid, CancellationToken cancellationToken = default);
/// <summary>
/// Move email to specified folder.
/// </summary>
Task MoveToFolderAsync(int uid, string folderName, CancellationToken cancellationToken = default);
/// <summary>
/// Delete email.
/// </summary>
Task DeleteEmailAsync(int uid, CancellationToken cancellationToken = default);
/// <summary>
/// Disconnect from IMAP server.
/// </summary>
Task DisconnectImapAsync(CancellationToken cancellationToken = default);
/// <summary>
/// Send email via SMTP.
/// </summary>
Task SendEmailAsync(MimeMessage message, string smtpServer, int smtpPort, string username, string password, bool useSsl = true, CancellationToken cancellationToken = default);
/// <summary>
/// Send email via SMTP using OAuth2.
/// </summary>
Task SendEmailOAuth2Async(MimeMessage message, string smtpServer, int smtpPort, string username, string accessToken, bool useSsl = true, CancellationToken cancellationToken = default);
/// <summary>
/// Get OAuth2 access token for Microsoft 365.
/// </summary>
Task<string> GetOAuth2AccessTokenAsync(string tenantId, string clientId, string clientSecret, string[] scopes, CancellationToken cancellationToken = default);
}

View File

@@ -1,28 +0,0 @@
namespace DigitalData.EmailProfiler.Application.Interfaces.Services;
/// <summary>
/// Encryption service interface for sensitive data (passwords, OAuth tokens).
/// Uses ASP.NET Core Data Protection API.
/// </summary>
public interface IEncryptionService
{
/// <summary>
/// Encrypt a string value.
/// </summary>
string Encrypt(string plainText);
/// <summary>
/// Decrypt an encrypted string value.
/// </summary>
string Decrypt(string cipherText);
/// <summary>
/// Encrypt a byte array.
/// </summary>
byte[] Encrypt(byte[] plainData);
/// <summary>
/// Decrypt an encrypted byte array.
/// </summary>
byte[] Decrypt(byte[] cipherData);
}

View File

@@ -1,59 +0,0 @@
namespace DigitalData.EmailProfiler.Application.Interfaces.Services;
/// <summary>
/// PDF processing service interface for validation and embedded file extraction.
/// </summary>
public interface IPdfProcessingService
{
/// <summary>
/// Validate if file is a valid PDF.
/// </summary>
Task<bool> IsValidPdfAsync(string filePath, CancellationToken cancellationToken = default);
/// <summary>
/// Validate if stream contains a valid PDF.
/// </summary>
Task<bool> IsValidPdfAsync(Stream stream, CancellationToken cancellationToken = default);
/// <summary>
/// Extract embedded files from PDF (e.g., ZUGFeRD XML).
/// </summary>
Task<IEnumerable<EmbeddedFile>> ExtractEmbeddedFilesAsync(string pdfPath, CancellationToken cancellationToken = default);
/// <summary>
/// Extract embedded files from PDF stream.
/// </summary>
Task<IEnumerable<EmbeddedFile>> ExtractEmbeddedFilesAsync(Stream pdfStream, CancellationToken cancellationToken = default);
/// <summary>
/// Check if PDF contains ZUGFeRD data.
/// </summary>
Task<bool> HasZugFeRDDataAsync(string pdfPath, CancellationToken cancellationToken = default);
/// <summary>
/// Extract ZUGFeRD XML from PDF.
/// </summary>
Task<string?> ExtractZugFeRDXmlAsync(string pdfPath, CancellationToken cancellationToken = default);
/// <summary>
/// Get PDF metadata (title, author, creation date, etc.).
/// </summary>
Task<PdfMetadata> GetMetadataAsync(string pdfPath, CancellationToken cancellationToken = default);
}
/// <summary>
/// Represents an embedded file extracted from a PDF.
/// </summary>
public record EmbeddedFile(string FileName, byte[] Content, string? Description = null);
/// <summary>
/// Represents PDF metadata.
/// </summary>
public record PdfMetadata(
string? Title,
string? Author,
string? Subject,
string? Keywords,
DateTime? CreationDate,
DateTime? ModificationDate,
int PageCount);