use topcoat::{
context::Cx,
router::{Body, error::forbidden},
view::BoxView,
};
use super::{
super::{
forms::{parse_form_body, truthy},
gate::gate,
write::commit_write,
},
fetch::find_by_key,
};
use crate::{
db::db,
policy::Ability,
resource::{Committed, Resource},
topcoat_compat::async_page,
};
const WRITE_DELETE: &str = "delete the record";
pub(crate) fn resource_delete<R: Resource>(cx: &Cx, body: Body) -> BoxView<'_> {
async_page(async move {
let resource = gate::<R>(cx)?;
if !resource.can(cx, Ability::DeleteAny) {
return Err(forbidden().into());
}
let values = parse_form_body(cx, body).await?.values;
crate::csrf::verify(cx, &values)?;
let confirmed = values.get("confirm").is_some_and(|v| truthy(v));
if !confirmed {
return Err(topcoat::router::error::bad_request("delete requires confirmation").into());
}
let mut db = db(cx);
let mut tx = db.transaction().await.map_err(crate::error::unavailable)?;
let id = topcoat::router::path_param_segment(cx, "id").to_string();
let record = find_by_key(cx, &resource, &id, &mut tx).await?;
if !resource.can(cx, Ability::View(&record)) {
return Err(forbidden().into());
}
if !resource.can(cx, Ability::Delete(&record)) {
return Err(forbidden().into());
}
let written = R::delete_record(cx, &record, &mut tx)
.await
.map(|()| vec![record]);
commit_write(
cx,
&resource,
tx,
written,
Committed::deleted,
"Deleted",
WRITE_DELETE,
)
.await
})
}
#[cfg(test)]
mod tests;