79 lines
2.6 KiB
Rust
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))
|
|
}
|
|
}
|