diff --git a/geneit_backend/src/controllers/accommodations_reservations_calendars_controller.rs b/geneit_backend/src/controllers/accommodations_reservations_calendars_controller.rs index a67c805..e9b85bd 100644 --- a/geneit_backend/src/controllers/accommodations_reservations_calendars_controller.rs +++ b/geneit_backend/src/controllers/accommodations_reservations_calendars_controller.rs @@ -1,7 +1,8 @@ use crate::constants::StaticConstraints; use crate::controllers::HttpResult; +use crate::extractors::accommodation_reservation_calendar_extractor::FamilyAndAccommodationReservationCalendarInPath; use crate::extractors::family_extractor::FamilyInPath; -use crate::models::AccommodationID; +use crate::models::{AccommodationID, AccommodationReservationCalendarID}; use crate::services::{accommodations_list_service, accommodations_reservations_calendars_service}; use actix_web::{web, HttpResponse}; @@ -51,10 +52,16 @@ pub async fn create(a: FamilyInPath, req: web::Json) -> Htt Ok(HttpResponse::Ok().json(calendar)) } -/// Get the list of calendars of the user +/// Get the list of calendars of a user pub async fn get_list(a: FamilyInPath) -> HttpResult { let users = accommodations_reservations_calendars_service::get_all_of_user(a.user_id(), a.family_id()) .await?; Ok(HttpResponse::Ok().json(users)) } + +/// Delete a calendar +pub async fn delete(resa: FamilyAndAccommodationReservationCalendarInPath) -> HttpResult { + accommodations_reservations_calendars_service::delete(resa.to_reservation()).await?; + Ok(HttpResponse::Ok().json("Calendar successfully deleted")) +} diff --git a/geneit_backend/src/extractors/accommodation_reservation_calendar_extractor.rs b/geneit_backend/src/extractors/accommodation_reservation_calendar_extractor.rs new file mode 100644 index 0000000..449be99 --- /dev/null +++ b/geneit_backend/src/extractors/accommodation_reservation_calendar_extractor.rs @@ -0,0 +1,93 @@ +use crate::extractors::family_extractor::FamilyInPath; +use crate::models::{ + AccommodationReservationCalendar, AccommodationReservationCalendarID, FamilyID, Membership, +}; +use crate::services::accommodations_reservations_calendars_service; +use actix_web::dev::Payload; +use actix_web::{FromRequest, HttpRequest}; +use serde::Deserialize; +use std::ops::Deref; + +#[derive(thiserror::Error, Debug)] +enum AccommodationCalendarExtractorErr { + #[error("Calendar {0:?} does not belong to user or family {1:?}!")] + CalendarNotOfUserOrFamily(AccommodationReservationCalendarID, FamilyID), +} + +#[derive(Debug)] +pub struct FamilyAndAccommodationReservationCalendarInPath( + Membership, + AccommodationReservationCalendar, +); + +impl FamilyAndAccommodationReservationCalendarInPath { + async fn load_calendar_from_path( + family: FamilyInPath, + calendar_id: AccommodationReservationCalendarID, + ) -> anyhow::Result { + let accommodation = + accommodations_reservations_calendars_service::get_by_id(calendar_id).await?; + if accommodation.family_id() != family.family_id() + || accommodation.user_id() != family.user_id() + { + return Err( + AccommodationCalendarExtractorErr::CalendarNotOfUserOrFamily( + accommodation.id(), + family.family_id(), + ) + .into(), + ); + } + + Ok(Self(family.into(), accommodation)) + } +} + +impl Deref for FamilyAndAccommodationReservationCalendarInPath { + type Target = AccommodationReservationCalendar; + + fn deref(&self) -> &Self::Target { + &self.1 + } +} + +impl FamilyAndAccommodationReservationCalendarInPath { + pub fn membership(&self) -> &Membership { + &self.0 + } + + pub fn to_reservation(self) -> AccommodationReservationCalendar { + self.1 + } +} + +#[derive(Deserialize)] +struct AccommodationIDInPath { + cal_id: AccommodationReservationCalendarID, +} + +impl FromRequest for FamilyAndAccommodationReservationCalendarInPath { + type Error = actix_web::Error; + type Future = futures_util::future::LocalBoxFuture<'static, Result>; + + fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future { + let req = req.clone(); + Box::pin(async move { + let family = FamilyInPath::extract(&req).await?; + + let accommodation_id = actix_web::web::Path::::from_request( + &req, + &mut Payload::None, + ) + .await? + .cal_id; + + Self::load_calendar_from_path(family, accommodation_id) + .await + .map_err(|e| { + log::error!("Failed to extract calendar ID from URL! {}", e); + actix_web::error::ErrorNotFound("Could not fetch calendar information!") + }) + }) + } +} diff --git a/geneit_backend/src/extractors/mod.rs b/geneit_backend/src/extractors/mod.rs index 868b912..05594ba 100644 --- a/geneit_backend/src/extractors/mod.rs +++ b/geneit_backend/src/extractors/mod.rs @@ -1,4 +1,5 @@ pub mod accommodation_extractor; +pub mod accommodation_reservation_calendar_extractor; pub mod accommodation_reservation_extractor; pub mod couple_extractor; pub mod family_extractor; diff --git a/geneit_backend/src/main.rs b/geneit_backend/src/main.rs index 1abc649..940ce31 100644 --- a/geneit_backend/src/main.rs +++ b/geneit_backend/src/main.rs @@ -266,7 +266,10 @@ async fn main() -> std::io::Result<()> { "/family/{id}/accommodations/reservations_calendars/list", web::get().to(accommodations_reservations_calendars_controller::get_list), ) - // TODO : delete + .route( + "/family/{id}/accommodations/reservations_calendars/{cal_id}", + web::delete().to(accommodations_reservations_calendars_controller::delete), + ) // TODO : anonymous URL access // Photos controller .route( diff --git a/geneit_backend/src/models.rs b/geneit_backend/src/models.rs index 3896239..80e9ec5 100644 --- a/geneit_backend/src/models.rs +++ b/geneit_backend/src/models.rs @@ -544,6 +544,24 @@ pub struct AccommodationReservationCalendar { pub time_used: i64, } +impl AccommodationReservationCalendar { + pub fn id(&self) -> AccommodationReservationCalendarID { + AccommodationReservationCalendarID(self.id) + } + + pub fn accommodation_id(&self) -> Option { + self.accommodation_id.map(AccommodationID) + } + + pub fn family_id(&self) -> FamilyID { + FamilyID(self.family_id) + } + + pub fn user_id(&self) -> UserID { + UserID(self.user_id) + } +} + #[derive(Insertable)] #[diesel(table_name = accommodations_reservations_cals_urls)] pub struct NewAccommodationReservationCalendar { diff --git a/geneit_backend/src/services/accommodations_reservations_calendars_service.rs b/geneit_backend/src/services/accommodations_reservations_calendars_service.rs index 20cc6e3..296292d 100644 --- a/geneit_backend/src/services/accommodations_reservations_calendars_service.rs +++ b/geneit_backend/src/services/accommodations_reservations_calendars_service.rs @@ -1,8 +1,8 @@ use crate::connections::db_connection; use crate::constants; use crate::models::{ - AccommodationID, AccommodationReservationCalendar, FamilyID, - NewAccommodationReservationCalendar, UserID, + AccommodationID, AccommodationReservationCalendar, AccommodationReservationCalendarID, + FamilyID, NewAccommodationReservationCalendar, UserID, }; use crate::schema::accommodations_reservations_cals_urls; use crate::utils::string_utils::rand_str; @@ -49,3 +49,28 @@ pub async fn get_all_of_user( .get_results(conn) }) } + +/// Get a single calendar by its id +pub async fn get_by_id( + id: AccommodationReservationCalendarID, +) -> anyhow::Result { + db_connection::execute(|conn| { + accommodations_reservations_cals_urls::table + .filter(accommodations_reservations_cals_urls::dsl::id.eq(id.0)) + .get_result(conn) + }) +} + +/// Delete a calendar +pub async fn delete(r: AccommodationReservationCalendar) -> anyhow::Result<()> { + // Remove the reservation + db_connection::execute(|conn| { + diesel::delete( + accommodations_reservations_cals_urls::dsl::accommodations_reservations_cals_urls + .filter(accommodations_reservations_cals_urls::dsl::id.eq(r.id().0)), + ) + .execute(conn) + })?; + + Ok(()) +}