diff --git a/astroplant-api/src/bin/astroplant-api.rs b/astroplant-api/src/bin/astroplant-api.rs index f5867e9..77765f0 100644 --- a/astroplant-api/src/bin/astroplant-api.rs +++ b/astroplant-api/src/bin/astroplant-api.rs @@ -86,6 +86,10 @@ async fn main() -> anyhow::Result<()> { "/kit-configurations/:kit_configuration_id/peripherals", post(kit_configuration::add_peripheral_to_configuration), ) + .route( + "/kit-configurations/:kit_configuration_id", + delete(kit_configuration::delete_configuration), + ) .nest( "/kit-rpc", Router::new() diff --git a/astroplant-api/src/controllers/kit_configuration/kit_configuration.rs b/astroplant-api/src/controllers/kit_configuration/kit_configuration.rs index 9f532d9..7bfd164 100644 --- a/astroplant-api/src/controllers/kit_configuration/kit_configuration.rs +++ b/astroplant-api/src/controllers/kit_configuration/kit_configuration.rs @@ -8,7 +8,7 @@ use crate::models::{Kit, KitConfigurationId, Peripheral}; use crate::problem::{self, Problem}; use crate::response::{Response, ResponseBuilder}; use crate::utils::deserialize_some; -use crate::{authorization, helpers, models, views}; +use crate::{authorization, helpers, models, schema, views}; use super::get_models_from_kit_configuration_id; @@ -243,3 +243,58 @@ pub async fn patch_configuration( Ok(ResponseBuilder::ok().body(views::KitConfiguration::from(patched_configuration))) } + +/// Handles the `DELETE /kit-configurations/{kitConfigurationId}` route. +/// +/// All peripherals, raw measurements, and aggregate measurements belonging to this configuration +/// are deleted. Media belonging to this configuration are orphaned and placed in the +/// media-pending-deletion queue. +pub async fn delete_configuration( + Extension(pg): Extension, + user_id: Option, + Path(kit_configuration_id): Path, +) -> Result { + let kit_configuration_id = models::KitConfigurationId(kit_configuration_id); + + let (kit, _kit_configuration) = + super::get_models_from_kit_configuration_id(pg.clone(), kit_configuration_id).await?; + super::authorize( + pg.clone(), + user_id, + &kit, + authorization::KitAction::EditConfiguration, + ) + .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_configuration_id.eq(kit_configuration_id.0)); + + // 1. Move media belonging to this configuration to 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 configuration itself. + kit_configuration_id.delete(conn) + })?; + + Ok::<_, Problem>(ResponseBuilder::ok().empty()) + }) + .await +} diff --git a/astroplant-api/src/controllers/kit_configuration/mod.rs b/astroplant-api/src/controllers/kit_configuration/mod.rs index 3330007..092773a 100644 --- a/astroplant-api/src/controllers/kit_configuration/mod.rs +++ b/astroplant-api/src/controllers/kit_configuration/mod.rs @@ -2,7 +2,7 @@ mod kit_configuration; mod peripheral; pub use kit_configuration::{ - configurations_by_kit_serial, create_configuration, patch_configuration, + configurations_by_kit_serial, create_configuration, delete_configuration, patch_configuration, }; pub use peripheral::{add_peripheral_to_configuration, delete_peripheral, patch_peripheral}; diff --git a/astroplant-api/src/models/kit_configuration.rs b/astroplant-api/src/models/kit_configuration.rs index d3a6982..6e53772 100644 --- a/astroplant-api/src/models/kit_configuration.rs +++ b/astroplant-api/src/models/kit_configuration.rs @@ -11,6 +11,12 @@ use super::{Kit, KitId}; #[diesel(table_name = kit_configurations)] pub struct KitConfigurationId(#[diesel(column_name = id)] pub i32); +impl KitConfigurationId { + pub fn delete(&self, conn: &mut PgConnection) -> QueryResult { + diesel::delete(self).execute(conn).map(|r| r > 0) + } +} + #[derive(Clone, Debug, PartialEq, Queryable, Identifiable, Associations)] #[diesel( table_name = kit_configurations, diff --git a/astroplant-api/src/schema.rs b/astroplant-api/src/schema.rs index 7aa14e6..d3b07fa 100644 --- a/astroplant-api/src/schema.rs +++ b/astroplant-api/src/schema.rs @@ -457,6 +457,44 @@ diesel::table! { } } +diesel::table! { + /// Representation of the `queue_media_pending_deletion` table. + /// + /// (Automatically generated by Diesel.) + queue_media_pending_deletion (id) { + /// The `id` column of the `queue_media_pending_deletion` table. + /// + /// Its SQL type is `Int4`. + /// + /// (Automatically generated by Diesel.) + id -> Int4, + /// The `created_at` column of the `queue_media_pending_deletion` table. + /// + /// Its SQL type is `Timestamptz`. + /// + /// (Automatically generated by Diesel.) + created_at -> Timestamptz, + /// The `media_id` column of the `queue_media_pending_deletion` table. + /// + /// Its SQL type is `Uuid`. + /// + /// (Automatically generated by Diesel.) + media_id -> Uuid, + /// The `media_datetime` column of the `queue_media_pending_deletion` table. + /// + /// Its SQL type is `Timestamptz`. + /// + /// (Automatically generated by Diesel.) + media_datetime -> Timestamptz, + /// The `media_size` column of the `queue_media_pending_deletion` table. + /// + /// Its SQL type is `Int8`. + /// + /// (Automatically generated by Diesel.) + media_size -> Int8, + } +} + diesel::table! { /// Representation of the `raw_measurements` table. /// @@ -592,6 +630,7 @@ diesel::allow_tables_to_appear_in_same_query!( peripheral_definitions, peripherals, quantity_types, + queue_media_pending_deletion, raw_measurements, users, ); diff --git a/migrations/2023-09-01-141454_add-queue-media-deletion/down.sql b/migrations/2023-09-01-141454_add-queue-media-deletion/down.sql new file mode 100644 index 0000000..76eecfa --- /dev/null +++ b/migrations/2023-09-01-141454_add-queue-media-deletion/down.sql @@ -0,0 +1 @@ +DROP TABLE queue_media_pending_deletion; diff --git a/migrations/2023-09-01-141454_add-queue-media-deletion/up.sql b/migrations/2023-09-01-141454_add-queue-media-deletion/up.sql new file mode 100644 index 0000000..6cdb2ee --- /dev/null +++ b/migrations/2023-09-01-141454_add-queue-media-deletion/up.sql @@ -0,0 +1,8 @@ +CREATE TABLE queue_media_pending_deletion ( + id serial PRIMARY KEY, + created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP, + media_id uuid NOT NULL UNIQUE, + media_datetime timestamptz NOT NULL, + media_size int8 NOT NULL, + CONSTRAINT media_size_positive CHECK ((media_size >= 0)) +);