Skip to main content

valence_core/
admin_entity_delete.rs

1//! Table-keyed queued delete for admin tooling.
2
3use crate::deletion::dag::table_skips_pending_deletion_filter;
4use crate::deletion::dag::DeletionDag;
5use crate::deletion::{dispatch, DeletionRequest, DeletionService};
6use crate::error::{Error, Result};
7use crate::ownership;
8use crate::privacy::{PrivacyEvaluator, PrivacyOperation};
9use crate::query::QueryCore;
10use crate::runtime::Valence;
11use crate::schema::SchemaRegistry;
12
13pub async fn queue_delete_entity(table: &str, id: &str, v: &Valence) -> Result<()> {
14    if table_skips_pending_deletion_filter(table) {
15        return Err(Error::Validation(format!(
16            "queued delete is not supported for table {table:?}"
17        )));
18    }
19
20    let registry = SchemaRegistry::global();
21    let schema = registry
22        .get_schema(table)
23        .ok_or_else(|| Error::NotFound(format!("unknown table {table}")))?;
24
25    let existing = match QueryCore::get_record_json(table, id, v).await? {
26        Some(j) => j,
27        None => return Ok(()),
28    };
29
30    PrivacyEvaluator::check_entity_read(schema, &existing, v).await?;
31    PrivacyEvaluator::check_entity_access(schema, PrivacyOperation::Delete, &existing, v).await?;
32
33    let bare = ownership::normalize_record_id_for_ownership(id);
34    if let Ok(Some(ownership)) =
35        ownership::OwnershipService::get_ownership_json(table, &bare, v).await
36    {
37        if ownership.get("status").and_then(|s| s.as_str()) == Some("pending_deletion") {
38            return Ok(());
39        }
40    }
41
42    let dag = DeletionDag::compute(table, &bare, v).await?;
43    if !dag.restrict_violations.is_empty() {
44        #[cfg(feature = "instrumentation")]
45        for v in &dag.restrict_violations {
46            crate::instrumentation::record_restrict_blocked(
47                table,
48                &bare,
49                &v.connection_name,
50                v.blocking_record_count.max(0) as usize,
51            );
52        }
53        return Err(Error::Validation(format!(
54            "delete restricted: {:?}",
55            dag.restrict_violations
56        )));
57    }
58
59    ownership::OwnershipService::mark_pending_deletion(table, &bare, v).await?;
60
61    let actor_json = serde_json::to_value(v.actor()).unwrap_or(serde_json::Value::Null);
62    let run_id = DeletionService::create_run(table, &bare, actor_json.clone(), v).await?;
63    #[cfg(feature = "instrumentation")]
64    {
65        let max_depth = dag.nodes.iter().map(|n| n.depth).max().unwrap_or(0) as usize;
66        crate::instrumentation::record_run_queued(table, &bare, dag.nodes.len(), max_depth);
67    }
68    dispatch(DeletionRequest {
69        run_id,
70        root_table: table.to_string(),
71        root_record_id: bare,
72        actor_json,
73    })
74    .await?;
75
76    Ok(())
77}