Files
k-photos/crates/application/src/catalog/queries/get_asset.rs

79 lines
2.6 KiB
Rust

use crate::catalog::visibility::VisibilityFilteredAssetRepository;
use domain::{
catalog::entities::Asset,
catalog::services::resolve_metadata,
errors::DomainError,
ports::{AssetMetadataRepository, AssetRepository, ShareRepository},
value_objects::{StructuredData, SystemId},
};
use std::sync::Arc;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct GetAssetQuery {
pub asset_id: SystemId,
pub user_id: SystemId,
}
pub struct GetAssetHandler {
asset_repo: Arc<dyn AssetRepository>,
metadata_repo: Arc<dyn AssetMetadataRepository>,
share_repo: Option<Arc<dyn ShareRepository>>,
}
impl GetAssetHandler {
pub fn new(
asset_repo: Arc<dyn AssetRepository>,
metadata_repo: Arc<dyn AssetMetadataRepository>,
) -> Self {
Self {
asset_repo,
metadata_repo,
share_repo: None,
}
}
/// Enable sharing-aware visibility filtering. When set, the handler
/// wraps the inner `AssetRepository` with a `VisibilityFilteredAssetRepository`
/// so that shared assets are visible to the caller.
pub fn with_visibility_filter(mut self, share_repo: Arc<dyn ShareRepository>) -> Self {
self.share_repo = Some(share_repo);
self
}
/// Returns the effective asset repo — wrapped with a visibility filter
/// when a `ShareRepository` has been configured, otherwise the raw inner repo.
fn effective_repo(&self, caller_id: SystemId) -> Arc<dyn AssetRepository> {
match &self.share_repo {
Some(share_repo) => Arc::new(VisibilityFilteredAssetRepository::new(
self.asset_repo.clone(),
share_repo.clone(),
caller_id,
)),
None => self.asset_repo.clone(),
}
}
pub async fn execute(
&self,
query: GetAssetQuery,
) -> Result<(Asset, StructuredData), DomainError> {
let repo = self.effective_repo(query.user_id);
let asset = repo
.find_by_id(&query.asset_id)
.await?
.ok_or_else(|| DomainError::NotFound(format!("Asset {} not found", query.asset_id)))?;
// When the visibility filter is active it already enforces access.
// When it is not, fall back to the original owner-only check.
if self.share_repo.is_none() && asset.owner_user_id != query.user_id {
return Err(DomainError::Forbidden("Access denied".to_string()));
}
let layers = self.metadata_repo.find_by_asset(&asset.asset_id).await?;
let resolved = resolve_metadata(&layers);
Ok((asset, resolved))
}
}