refactor: introduce IngestTransaction port to reduce IngestAssetHandler from 7 to 4 ports

This commit is contained in:
2026-05-31 18:44:51 +02:00
parent aa09aec66b
commit 0b2237860e
7 changed files with 189 additions and 67 deletions

View File

@@ -9,10 +9,10 @@ use domain::{
errors::DomainError,
ports::{
AlbumRepository, AssetMetadataRepository, AssetRepository, DuplicateRepository,
GroupRepository, IngestSessionRepository, JobBatchRepository, JobRepository,
LibraryPathRepository, PipelineRepository, PluginRepository, QuotaRepository,
RoleRepository, ShareRepository, SidecarRepository, StorageVolumeRepository, TagRepository,
UsageLedgerRepository, UserRepository,
GroupRepository, IngestSessionRepository, IngestTransaction, JobBatchRepository,
JobRepository, LibraryPathRepository, PipelineRepository, PluginRepository,
QuotaRepository, RoleRepository, ShareRepository, SidecarRepository,
StorageVolumeRepository, TagRepository, UsageLedgerRepository, UserRepository,
},
value_objects::{Checksum, DateTimeStamp, Email, SystemId},
};
@@ -1126,3 +1126,93 @@ impl PipelineRepository for InMemoryPipelineRepository {
Ok(())
}
}
// --- InMemoryIngestTransaction ---
pub struct InMemoryIngestTransaction {
assets: Mutex<HashMap<String, Asset>>,
sessions: Mutex<HashMap<String, IngestSession>>,
quotas: Mutex<HashMap<String, QuotaDefinition>>,
ledger: Mutex<Vec<UsageLedgerEntry>>,
}
impl InMemoryIngestTransaction {
pub fn new() -> Self {
Self {
assets: Mutex::new(HashMap::new()),
sessions: Mutex::new(HashMap::new()),
quotas: Mutex::new(HashMap::new()),
ledger: Mutex::new(Vec::new()),
}
}
/// Pre-seed a quota for testing.
pub async fn insert_quota(&self, quota: &QuotaDefinition) {
self.quotas
.lock()
.await
.insert(quota.owner_scope.to_string(), quota.clone());
}
}
impl Default for InMemoryIngestTransaction {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl IngestTransaction for InMemoryIngestTransaction {
async fn save_asset(&self, asset: &Asset) -> Result<(), DomainError> {
self.assets
.lock()
.await
.insert(asset.asset_id.to_string(), asset.clone());
Ok(())
}
async fn save_session(&self, session: &IngestSession) -> Result<(), DomainError> {
self.sessions
.lock()
.await
.insert(session.session_id.to_string(), session.clone());
Ok(())
}
async fn find_quota(
&self,
owner_id: &SystemId,
) -> Result<Option<QuotaDefinition>, DomainError> {
Ok(self
.quotas
.lock()
.await
.values()
.find(|q| &q.owner_scope == owner_id)
.cloned())
}
async fn sum_usage(
&self,
user_id: &SystemId,
usage_type: UsageType,
since: Option<DateTimeStamp>,
) -> Result<u64, DomainError> {
let entries = self.ledger.lock().await;
let total = entries
.iter()
.filter(|e| &e.user_id == user_id && e.usage_type == usage_type)
.filter(|e| match &since {
Some(ts) => &e.timestamp >= ts,
None => true,
})
.map(|e| e.consumed_amount)
.sum();
Ok(total)
}
async fn record_usage(&self, entry: &UsageLedgerEntry) -> Result<(), DomainError> {
self.ledger.lock().await.push(entry.clone());
Ok(())
}
}