diff --git a/geneit_backend/src/controllers/accommodations_reservations_controller.rs b/geneit_backend/src/controllers/accommodations_reservations_controller.rs index f76d491..516f25f 100644 --- a/geneit_backend/src/controllers/accommodations_reservations_controller.rs +++ b/geneit_backend/src/controllers/accommodations_reservations_controller.rs @@ -1,5 +1,6 @@ use crate::controllers::HttpResult; use crate::extractors::accommodation_extractor::FamilyAndAccommodationInPath; +use crate::extractors::accommodation_reservation_extractor::FamilyAndAccommodationReservationInPath; use crate::extractors::family_extractor::FamilyInPath; use crate::services::accommodations_reservations_service; use actix_web::HttpResponse; @@ -15,3 +16,8 @@ pub async fn full_list(m: FamilyInPath) -> HttpResult { Ok(HttpResponse::Ok() .json(accommodations_reservations_service::get_all_of_family(m.family_id()).await?)) } + +/// Get a single accommodation reservation +pub async fn get_single(m: FamilyAndAccommodationReservationInPath) -> HttpResult { + Ok(HttpResponse::Ok().json(m.to_reservation())) +} diff --git a/geneit_backend/src/extractors/accommodation_extractor.rs b/geneit_backend/src/extractors/accommodation_extractor.rs index 76bfca7..f772ddf 100644 --- a/geneit_backend/src/extractors/accommodation_extractor.rs +++ b/geneit_backend/src/extractors/accommodation_extractor.rs @@ -72,7 +72,7 @@ impl FromRequest for FamilyAndAccommodationInPath { .await? .accommodation_id; - FamilyAndAccommodationInPath::load_accommodation_from_path(family, accommodation_id) + Self::load_accommodation_from_path(family, accommodation_id) .await .map_err(|e| { log::error!("Failed to extract accommodation ID from URL! {}", e); diff --git a/geneit_backend/src/extractors/accommodation_reservation_extractor.rs b/geneit_backend/src/extractors/accommodation_reservation_extractor.rs new file mode 100644 index 0000000..483028f --- /dev/null +++ b/geneit_backend/src/extractors/accommodation_reservation_extractor.rs @@ -0,0 +1,99 @@ +use crate::extractors::family_extractor::FamilyInPath; +use crate::models::{ + Accommodation, AccommodationReservation, AccommodationReservationID, FamilyID, Membership, +}; +use crate::services::{accommodations_list_service, accommodations_reservations_service}; +use actix_web::dev::Payload; +use actix_web::{FromRequest, HttpRequest}; +use serde::Deserialize; +use std::fmt::Debug; +use std::ops::Deref; + +#[derive(thiserror::Error, Debug)] +enum AccommodationReservationExtractorErr { + #[error("Accommodation reservation {0:?} does not belong to family {1:?}!")] + AccommodationNotInFamily(AccommodationReservationID, FamilyID), +} + +#[derive(Debug)] +pub struct FamilyAndAccommodationReservationInPath( + Membership, + Accommodation, + AccommodationReservation, +); + +impl FamilyAndAccommodationReservationInPath { + async fn load_accommodation_reservation_from_path( + family: FamilyInPath, + reservation_id: AccommodationReservationID, + ) -> anyhow::Result { + let reservation = accommodations_reservations_service::get_by_id(reservation_id).await?; + let accommodation = + accommodations_list_service::get_by_id(reservation.accommodation_id()).await?; + + if accommodation.family_id() != family.family_id() + || reservation.family_id() != family.family_id() + { + return Err( + AccommodationReservationExtractorErr::AccommodationNotInFamily( + reservation.id(), + family.family_id(), + ) + .into(), + ); + } + + Ok(Self(family.into(), accommodation, reservation)) + } +} + +impl Deref for FamilyAndAccommodationReservationInPath { + type Target = AccommodationReservation; + + fn deref(&self) -> &Self::Target { + &self.2 + } +} + +impl FamilyAndAccommodationReservationInPath { + pub fn membership(&self) -> &Membership { + &self.0 + } + + pub fn to_accommodation(self) -> Accommodation { + self.1 + } + + pub fn to_reservation(self) -> AccommodationReservation { + self.2 + } +} + +#[derive(Deserialize)] +struct ReservationIDInPath { + reservation_id: AccommodationReservationID, +} + +impl FromRequest for FamilyAndAccommodationReservationInPath { + 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 reservation_id = + actix_web::web::Path::::from_request(&req, &mut Payload::None) + .await? + .reservation_id; + + Self::load_accommodation_reservation_from_path(family, reservation_id) + .await + .map_err(|e| { + log::error!("Failed to extract accommodation ID from URL! {}", e); + actix_web::error::ErrorNotFound("Could not fetch accommodation information!") + }) + }) + } +} diff --git a/geneit_backend/src/extractors/mod.rs b/geneit_backend/src/extractors/mod.rs index 8859615..868b912 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_extractor; pub mod couple_extractor; pub mod family_extractor; pub mod member_extractor; diff --git a/geneit_backend/src/main.rs b/geneit_backend/src/main.rs index 8f1ca0e..be557d3 100644 --- a/geneit_backend/src/main.rs +++ b/geneit_backend/src/main.rs @@ -227,10 +227,6 @@ async fn main() -> std::io::Result<()> { web::delete().to(accommodations_list_controller::delete), ) // [ACCOMODATIONS] Reservations controller - // TODO : create - // TODO : get single - // TODO : update - // TODO : delete .route( "/family/{id}/accommodations/reservations/accommodation/{accommodation_id}", web::get() @@ -240,6 +236,13 @@ async fn main() -> std::io::Result<()> { "/family/{id}/accommodations/reservations/full_list", web::get().to(accommodations_reservations_controller::full_list), ) + // TODO : create + .route( + "/family/{id}/accommodations/reservation/{reservation_id}", + web::get().to(accommodations_reservations_controller::get_single), + ) + // TODO : update + // TODO : delete // TODO : validate or reject // Photos controller .route( diff --git a/geneit_backend/src/models.rs b/geneit_backend/src/models.rs index f30bd1b..2acf822 100644 --- a/geneit_backend/src/models.rs +++ b/geneit_backend/src/models.rs @@ -498,6 +498,20 @@ pub struct AccommodationReservation { pub validated: Option, } +impl AccommodationReservation { + pub fn id(&self) -> AccommodationReservationID { + AccommodationReservationID(self.id) + } + + pub fn accommodation_id(&self) -> AccommodationID { + AccommodationID(self.accommodation_id) + } + + pub fn family_id(&self) -> FamilyID { + FamilyID(self.family_id) + } +} + #[derive(Insertable)] #[diesel(table_name = accommodations_reservations)] pub struct NewAccommodationReservation { diff --git a/geneit_backend/src/services/accommodations_reservations_service.rs b/geneit_backend/src/services/accommodations_reservations_service.rs index ca76976..95dbf05 100644 --- a/geneit_backend/src/services/accommodations_reservations_service.rs +++ b/geneit_backend/src/services/accommodations_reservations_service.rs @@ -1,5 +1,7 @@ use crate::connections::db_connection; -use crate::models::{AccommodationID, AccommodationReservation, FamilyID}; +use crate::models::{ + AccommodationID, AccommodationReservation, AccommodationReservationID, FamilyID, +}; use crate::schema::accommodations_reservations; use diesel::prelude::*; @@ -22,3 +24,12 @@ pub async fn get_all_of_family(id: FamilyID) -> anyhow::Result anyhow::Result { + db_connection::execute(|conn| { + accommodations_reservations::table + .filter(accommodations_reservations::dsl::id.eq(id.0)) + .get_result(conn) + }) +}