Skip to main content

elasticctl_api/dashboards_ops/
delete.rs

1use super::{
2    BTreeSet, DashboardSummary, DeleteOutcome, Error, ErrorKind, MutationPlan, Result, Transport,
3    dashboards, invalid_plan, resolve,
4};
5
6#[derive(Debug, Clone, PartialEq)]
7pub struct DashboardDeletePlan {
8    pub preview: MutationPlan,
9    pub targets: Vec<DashboardSummary>,
10}
11pub async fn plan_delete(
12    transport: &Transport,
13    selectors: &[String],
14) -> Result<DashboardDeletePlan> {
15    if selectors.is_empty() {
16        return Err(Error::new(
17            ErrorKind::Error,
18            "dashboard delete needs at least one dashboard",
19        ));
20    }
21    let mut seen = BTreeSet::new();
22    let mut targets = Vec::new();
23    for selector in selectors {
24        let dashboard = resolve(transport, selector).await?;
25        if seen.insert(dashboard.id.clone()) {
26            targets.push(dashboard);
27        }
28    }
29    Ok(DashboardDeletePlan {
30        preview: delete_preview(&targets),
31        targets,
32    })
33}
34
35pub async fn apply_delete(
36    transport: &Transport,
37    plan: &DashboardDeletePlan,
38) -> Result<DeleteOutcome> {
39    validate_delete_plan(plan)?;
40    let mut deleted = Vec::new();
41    let mut failed = Vec::new();
42    for target in &plan.targets {
43        match dashboards::delete(transport, &target.id).await {
44            Ok(()) => deleted.push(serde_json::json!({"id": target.id})),
45            Err(error) => failed.push(serde_json::json!({
46                "id": target.id,
47                "error": error.message,
48            })),
49        }
50    }
51    Ok(DeleteOutcome {
52        applied: true,
53        deleted,
54        failed,
55        total: plan.targets.len(),
56    })
57}
58
59fn delete_preview(targets: &[DashboardSummary]) -> MutationPlan {
60    MutationPlan {
61        preview_action: format!("Delete {} dashboard(s)", targets.len()),
62        preview_details: targets
63            .iter()
64            .map(|dashboard| format!("{}  {}", dashboard.id, dashboard.title))
65            .collect(),
66        targets: targets
67            .iter()
68            .map(|dashboard| dashboard.id.clone())
69            .collect(),
70    }
71}
72
73fn validate_delete_plan(plan: &DashboardDeletePlan) -> Result<()> {
74    if plan.targets.is_empty() {
75        return invalid_plan("dashboard delete plan needs at least one target");
76    }
77    let mut ids = BTreeSet::new();
78    for target in &plan.targets {
79        if target.id.trim().is_empty() || target.title.trim().is_empty() {
80            return invalid_plan("dashboard delete target identity must not be empty");
81        }
82        if !ids.insert(target.id.clone()) {
83            return invalid_plan("dashboard delete targets must be unique by id");
84        }
85    }
86    if plan.preview != delete_preview(&plan.targets) {
87        return invalid_plan("dashboard delete preview does not match guarded targets");
88    }
89    Ok(())
90}