refactor(application): Remove old repository and service interfaces
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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<T>
|
||||
/// 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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
Reference in New Issue
Block a user