From 4997132bf83606eaf405f2a4a30a967fe300d13f Mon Sep 17 00:00:00 2001 From: Pierre Hubert Date: Mon, 7 Aug 2023 17:16:00 +0200 Subject: [PATCH] Can get couple information --- .../src/controllers/couples_controller.rs | 35 +++++++- .../src/extractors/couple_extractor.rs | 79 +++++++++++++++++++ geneit_backend/src/extractors/mod.rs | 1 + geneit_backend/src/main.rs | 8 ++ 4 files changed, 122 insertions(+), 1 deletion(-) create mode 100644 geneit_backend/src/extractors/couple_extractor.rs diff --git a/geneit_backend/src/controllers/couples_controller.rs b/geneit_backend/src/controllers/couples_controller.rs index 792a8d6..c9143a8 100644 --- a/geneit_backend/src/controllers/couples_controller.rs +++ b/geneit_backend/src/controllers/couples_controller.rs @@ -1,7 +1,8 @@ use crate::controllers::members_controller::RequestDate; use crate::controllers::HttpResult; +use crate::extractors::couple_extractor::FamilyAndCoupleInPath; use crate::extractors::family_extractor::FamilyInPath; -use crate::models::{Couple, CoupleState, MemberID}; +use crate::models::{Couple, CoupleState, MemberID, PhotoID}; use crate::services::{couples_service, members_service}; use actix_web::{web, HttpResponse}; @@ -79,6 +80,22 @@ impl CoupleRequest { } } +#[derive(serde::Serialize)] +struct CoupleAPI { + #[serde(flatten)] + member: Couple, + signed_photo_id: Option, +} + +impl CoupleAPI { + pub fn new(member: Couple) -> Self { + Self { + signed_photo_id: member.photo_id().as_ref().map(PhotoID::to_signed_hash), + member, + } + } +} + /// Create a new couple pub async fn create(m: FamilyInPath, req: web::Json) -> HttpResult { let mut couple = couples_service::create(m.family_id()).await?; @@ -97,3 +114,19 @@ pub async fn create(m: FamilyInPath, req: web::Json) -> HttpResul Ok(HttpResponse::Ok().json(couple)) } + +/// Get the entire list of couples +pub async fn get_all(m: FamilyInPath) -> HttpResult { + let couples = couples_service::get_all_of_family(m.family_id()) + .await? + .into_iter() + .map(CoupleAPI::new) + .collect::>(); + + Ok(HttpResponse::Ok().json(couples)) +} + +/// Get a single couple entry +pub async fn get_single(m: FamilyAndCoupleInPath) -> HttpResult { + Ok(HttpResponse::Ok().json(CoupleAPI::new(m.to_couple()))) +} diff --git a/geneit_backend/src/extractors/couple_extractor.rs b/geneit_backend/src/extractors/couple_extractor.rs new file mode 100644 index 0000000..97a58e6 --- /dev/null +++ b/geneit_backend/src/extractors/couple_extractor.rs @@ -0,0 +1,79 @@ +use crate::extractors::family_extractor::FamilyInPath; +use crate::models::{Couple, CoupleID, FamilyID, Membership}; +use crate::services::couples_service; +use actix_web::dev::Payload; +use actix_web::{FromRequest, HttpRequest}; +use serde::Deserialize; +use std::ops::Deref; + +#[derive(thiserror::Error, Debug)] +enum CoupleExtractorErr { + #[error("Couple {0:?} does not belong to family {1:?}!")] + CoupleNotInFamily(CoupleID, FamilyID), +} + +#[derive(Debug)] +pub struct FamilyAndCoupleInPath(Membership, Couple); + +impl FamilyAndCoupleInPath { + async fn load_couple_from_path( + family: FamilyInPath, + couple_id: CoupleID, + ) -> anyhow::Result { + let couple = couples_service::get_by_id(couple_id).await?; + if couple.family_id() != family.family_id() { + return Err( + CoupleExtractorErr::CoupleNotInFamily(couple.id(), family.family_id()).into(), + ); + } + + Ok(Self(family.into(), couple)) + } +} + +impl Deref for FamilyAndCoupleInPath { + type Target = Couple; + + fn deref(&self) -> &Self::Target { + &self.1 + } +} + +impl FamilyAndCoupleInPath { + pub fn membership(&self) -> &Membership { + &self.0 + } + + pub fn to_couple(self) -> Couple { + self.1 + } +} + +#[derive(Deserialize)] +struct CoupleIDInPath { + couple_id: CoupleID, +} + +impl FromRequest for FamilyAndCoupleInPath { + 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 couple_id = + actix_web::web::Path::::from_request(&req, &mut Payload::None) + .await? + .couple_id; + + FamilyAndCoupleInPath::load_couple_from_path(family, couple_id) + .await + .map_err(|e| { + log::error!("Failed to extract couple ID from URL! {}", e); + actix_web::error::ErrorNotFound("Could not fetch couple information!") + }) + }) + } +} diff --git a/geneit_backend/src/extractors/mod.rs b/geneit_backend/src/extractors/mod.rs index 11850c0..0cafc82 100644 --- a/geneit_backend/src/extractors/mod.rs +++ b/geneit_backend/src/extractors/mod.rs @@ -1,2 +1,3 @@ +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 f9b1a2f..a765a04 100644 --- a/geneit_backend/src/main.rs +++ b/geneit_backend/src/main.rs @@ -167,6 +167,14 @@ async fn main() -> std::io::Result<()> { "/family/{id}/couple/create", web::post().to(couples_controller::create), ) + .route( + "/family/{id}/couples", + web::get().to(couples_controller::get_all), + ) + .route( + "/family/{id}/couple/{couple_id}", + web::get().to(couples_controller::get_single), + ) // Photos controller .route( "/photo/{id}",