use serde_json::json;
use crate::error::{ApiError, Result};
use crate::models::{
AddTagRequest, Asset, AuditAction, CreateAssetRequest, Tag,
UpdateAssetRequest, UpdateAuthContextRequest,
};
use crate::service::Services;
pub struct AssetService;
impl AssetService {
pub async fn create(
services: &Services,
request: CreateAssetRequest,
user_id: &str,
) -> Result<Asset> {
if request.title.trim().is_empty() {
return Err(ApiError::Validation("Title cannot be empty".to_string()));
}
let asset = services.assets().create(request.clone(), user_id).await?;
services.audit().create(
"asset",
&asset.id,
AuditAction::Create,
json!({
"title": asset.title,
"tags": asset.tags.len(),
}),
user_id,
)
.await?;
Ok(asset)
}
pub async fn get(services: &Services, id: &str) -> Result<Asset> {
services.assets().get_by_id(id).await
}
pub async fn update(
services: &Services,
id: &str,
request: UpdateAssetRequest,
user_id: &str,
) -> Result<Asset> {
if let Some(ref title) = request.title {
if title.trim().is_empty() {
return Err(ApiError::Validation("Title cannot be empty".to_string()));
}
}
let old_asset = services.assets().get_by_id(id).await?;
let asset = services.assets().update(id, request.clone(), user_id).await?;
services.audit().create(
"asset",
id,
AuditAction::Update,
json!({
"old": {
"title": old_asset.title,
"content": old_asset.content,
},
"new": {
"title": request.title,
"content": request.content,
}
}),
user_id,
)
.await?;
Ok(asset)
}
pub async fn delete(services: &Services, id: &str, user_id: &str) -> Result<()> {
services.assets().get_by_id(id).await?;
services.assets().soft_delete(id).await?;
services.relations().delete_by_asset(id).await?;
services.collections().remove_asset_from_all(id).await?;
services.audit().create("asset", id, AuditAction::Delete, json!({}), user_id).await?;
Ok(())
}
pub async fn list(
services: &Services,
limit: i64,
cursor: Option<&str>,
asset_type_tag: Option<&str>,
sort_by: &str,
order: &str,
) -> Result<(Vec<Asset>, Option<String>)> {
services.assets().list(limit, cursor, asset_type_tag, sort_by, order).await
}
pub async fn add_tag(
services: &Services,
asset_id: &str,
request: AddTagRequest,
user_id: &str,
) -> Result<Tag> {
request.validate().map_err(ApiError::Validation)?;
let tag = services.assets().add_tag(asset_id, request.clone(), user_id).await?;
services.audit().create(
"asset",
asset_id,
AuditAction::AddTag,
json!({
"tag_id": tag.id,
"category": &tag.category,
"value": &tag.value,
}),
user_id,
)
.await?;
Ok(tag)
}
pub async fn remove_tag(
services: &Services,
asset_id: &str,
tag_id: &str,
user_id: &str,
) -> Result<()> {
services.assets().remove_tag(asset_id, tag_id).await?;
services.audit().create(
"asset",
asset_id,
AuditAction::RemoveTag,
json!({ "tag_id": tag_id }),
user_id,
)
.await?;
Ok(())
}
pub async fn update_auth_context(
services: &Services,
id: &str,
request: UpdateAuthContextRequest,
user_id: &str,
) -> Result<Asset> {
let asset = services.assets().get_by_id(id).await?;
let mut ctx = asset.auth_context.unwrap_or_default();
if let Some(ref visibility) = request.visibility {
ctx.visibility = visibility.clone();
}
if let Some(ref owner_groups) = request.owner_groups {
ctx.owner_groups = owner_groups.clone();
}
if let Some(ref confidentiality) = request.confidentiality {
ctx.confidentiality = confidentiality.clone();
}
let updated = services.assets().update_auth_context(id, &ctx).await?;
let cascaded_count = if request.cascade {
let descendants = services.relations().get_descendants(id).await?;
let count = descendants.len();
for child_id in &descendants {
if let Ok(child) = services.assets().get_by_id(child_id).await {
let mut child_ctx = child.auth_context.unwrap_or_default();
if request.visibility.is_some() {
child_ctx.visibility = ctx.visibility.clone();
}
if request.owner_groups.is_some() {
child_ctx.owner_groups = ctx.owner_groups.clone();
}
if request.confidentiality.is_some() {
child_ctx.confidentiality = ctx.confidentiality.clone();
}
let _ = services.assets().update_auth_context(child_id, &child_ctx).await;
}
}
count
} else {
0
};
services.audit().create(
"asset",
id,
AuditAction::Update,
json!({
"action": "update_auth_context",
"auth_context": &ctx,
"cascade": request.cascade,
"cascaded_count": cascaded_count,
}),
user_id,
)
.await?;
Ok(updated)
}
}