diff --git a/astroplant-api/src/authorization/mod.rs b/astroplant-api/src/authorization/mod.rs index 3eb75f5..410975b 100644 --- a/astroplant-api/src/authorization/mod.rs +++ b/astroplant-api/src/authorization/mod.rs @@ -39,6 +39,7 @@ pub trait Permission { pub enum KitAction { View, SubscribeRealTimeMeasurements, + Delete, ResetPassword, EditDetails, EditConfiguration, @@ -71,7 +72,7 @@ impl Permission for KitAction { UserWithMembership(_user, membership) => match self { View | SubscribeRealTimeMeasurements => true, EditDetails | EditConfiguration => membership.access_configure, - ResetPassword | EditMembers | SetSuperMember => membership.access_super, + Delete | ResetPassword | EditMembers | SetSuperMember => membership.access_super, RpcVersion | RpcUptime | RpcPeripheralCommand | RpcPeripheralCommandLock => { membership.access_super } diff --git a/astroplant-api/src/bin/astroplant-api.rs b/astroplant-api/src/bin/astroplant-api.rs index 77765f0..1584edd 100644 --- a/astroplant-api/src/bin/astroplant-api.rs +++ b/astroplant-api/src/bin/astroplant-api.rs @@ -61,8 +61,9 @@ async fn main() -> anyhow::Result<()> { .route("/kits", get(kit::kits)) .route("/kits", post(kit::create_kit)) .route("/kits/:kit_serial", get(kit::kit_by_serial)) - .route("/kits/:kit_serial/password", post(kit::reset_password)) .route("/kits/:kit_serial", patch(kit::patch_kit)) + .route("/kits/:kit_serial", delete(kit::delete_kit)) + .route("/kits/:kit_serial/password", post(kit::reset_password)) .route( "/kits/:kit_serial/configurations", get(kit_configuration::configurations_by_kit_serial), diff --git a/astroplant-api/src/controllers/kit/mod.rs b/astroplant-api/src/controllers/kit/mod.rs index ec4f1b9..de08477 100644 --- a/astroplant-api/src/controllers/kit/mod.rs +++ b/astroplant-api/src/controllers/kit/mod.rs @@ -6,7 +6,7 @@ use crate::database::PgPool; use crate::problem::{self, Problem}; use crate::response::{Response, ResponseBuilder}; use crate::utils::deserialize_some; -use crate::{helpers, models, views}; +use crate::{helpers, models, schema, views}; mod archive; pub use archive::{archive, archive_authorize}; @@ -193,3 +193,55 @@ pub async fn patch_kit( }) .await? } + +/// Handles the `DELETE /kits/{kitSerial}` route. +/// +/// All configurations, peripherals, raw measurements, and aggregate measurements belonging to this +/// kit are deleted. Media belonging to this kit are orphaned and placed in the +/// media-pending-deletion queue. +pub async fn delete_kit( + Extension(pg): Extension, + Path(kit_serial): Path, + user_id: Option, +) -> Result { + let (_, _, kit) = helpers::fut_kit_permission_or_forbidden( + pg.clone(), + user_id, + kit_serial, + crate::authorization::KitAction::Delete, + ) + .await?; + + let conn = pg.get().await?; + conn.interact_flatten_err(move |conn| { + use diesel::prelude::*; + use schema::media; + use schema::queue_media_pending_deletion; + + conn.transaction(|conn| { + let selected_media = media::dsl::media.filter(media::kit_id.eq(kit.id)); + + // 1. Move media belonging to this kit to the pending deletion queue. + selected_media + .select((media::id, media::datetime, media::size)) + .insert_into(queue_media_pending_deletion::table) + .into_columns(( + queue_media_pending_deletion::media_id, + queue_media_pending_deletion::media_datetime, + queue_media_pending_deletion::media_size, + )) + .execute(conn)?; + + // 2. Delete this media from the media table. + diesel::delete(selected_media).execute(conn)?; + + // 3. And finally delete the kit itself. + diesel::delete(&kit).execute(conn)?; + + Ok::<_, diesel::result::Error>(()) + })?; + + Ok::<_, Problem>(ResponseBuilder::ok().empty()) + }) + .await +} diff --git a/openapi.yaml b/openapi.yaml index 6081958..20cc3d4 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -153,6 +153,31 @@ paths: $ref: "#/components/responses/ErrorRateLimit" '500': $ref: "#/components/responses/ErrorInternalServer" + delete: + summary: Delete a kit. + operationId: deleteKit + security: + - bearerAuth: [] + tags: + - kits + parameters: + - name: kitSerial + in: path + required: true + description: The serial of the kit to patch. + schema: + type: string + responses: + '200': + description: The kit was successfully deleted. + '400': + $ref: "#/components/responses/InvalidJson" + '401': + $ref: "#/components/responses/ErrorUnauthorized" + '429': + $ref: "#/components/responses/ErrorRateLimit" + '500': + $ref: "#/components/responses/ErrorInternalServer" patch: summary: Update the kit details. operationId: patchKit