From 5f0842b7dc7a6e286cac30e3bb98ee4bd5e2e327 Mon Sep 17 00:00:00 2001 From: Pierre Hubert Date: Sat, 5 Aug 2023 10:24:19 +0200 Subject: [PATCH] Add GET /family/{id}/member/{id_member} --- .../src/controllers/members_controller.rs | 6 ++ .../src/extractors/family_extractor.rs | 6 ++ .../src/extractors/member_extractor.rs | 79 +++++++++++++++++++ geneit_backend/src/extractors/mod.rs | 1 + geneit_backend/src/main.rs | 4 + geneit_backend/src/models.rs | 2 +- 6 files changed, 97 insertions(+), 1 deletion(-) create mode 100644 geneit_backend/src/extractors/member_extractor.rs diff --git a/geneit_backend/src/controllers/members_controller.rs b/geneit_backend/src/controllers/members_controller.rs index 276be12..ad38211 100644 --- a/geneit_backend/src/controllers/members_controller.rs +++ b/geneit_backend/src/controllers/members_controller.rs @@ -1,6 +1,7 @@ use crate::constants::{SizeConstraint, StaticConstraints}; use crate::controllers::HttpResult; use crate::extractors::family_extractor::FamilyInPath; +use crate::extractors::member_extractor::FamilyAndMemberInPath; use crate::models::{Member, MemberID, Sex}; use crate::services::members_service; use crate::utils::countries_utils; @@ -240,3 +241,8 @@ pub async fn create(f: FamilyInPath, req: web::Json) -> HttpResul Ok(HttpResponse::Ok().json(member)) } + +/// Get the information of a single family member +pub async fn get_single(m: FamilyAndMemberInPath) -> HttpResult { + Ok(HttpResponse::Ok().json(m.to_member())) +} diff --git a/geneit_backend/src/extractors/family_extractor.rs b/geneit_backend/src/extractors/family_extractor.rs index 71ac68e..4be40a3 100644 --- a/geneit_backend/src/extractors/family_extractor.rs +++ b/geneit_backend/src/extractors/family_extractor.rs @@ -18,6 +18,12 @@ impl FamilyInPath { } } +impl From for Membership { + fn from(val: FamilyInPath) -> Self { + val.0 + } +} + impl Deref for FamilyInPath { type Target = Membership; diff --git a/geneit_backend/src/extractors/member_extractor.rs b/geneit_backend/src/extractors/member_extractor.rs new file mode 100644 index 0000000..b54633b --- /dev/null +++ b/geneit_backend/src/extractors/member_extractor.rs @@ -0,0 +1,79 @@ +use crate::extractors::family_extractor::FamilyInPath; +use crate::models::{FamilyID, Member, MemberID, Membership}; +use crate::services::members_service; +use actix_web::dev::Payload; +use actix_web::{FromRequest, HttpRequest}; +use serde::Deserialize; +use std::ops::Deref; + +#[derive(thiserror::Error, Debug)] +enum MemberExtractorErr { + #[error("Member {0:?} does not belong to family {1:?}!")] + MemberNotInFamily(MemberID, FamilyID), +} + +#[derive(Debug)] +pub struct FamilyAndMemberInPath(Membership, Member); + +impl FamilyAndMemberInPath { + async fn load_member_from_path( + family: FamilyInPath, + member_id: MemberID, + ) -> anyhow::Result { + let member = members_service::get_by_id(member_id).await?; + if member.family_id() != family.family_id() { + return Err( + MemberExtractorErr::MemberNotInFamily(member.id(), family.family_id()).into(), + ); + } + + Ok(Self(family.into(), member)) + } +} + +impl Deref for FamilyAndMemberInPath { + type Target = Member; + + fn deref(&self) -> &Self::Target { + &self.1 + } +} + +impl FamilyAndMemberInPath { + pub fn membership(&self) -> &Membership { + &self.0 + } + + pub fn to_member(self) -> Member { + self.1 + } +} + +#[derive(Deserialize)] +struct MemberIDInPath { + member_id: MemberID, +} + +impl FromRequest for FamilyAndMemberInPath { + 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 member_id = + actix_web::web::Path::::from_request(&req, &mut Payload::None) + .await? + .member_id; + + FamilyAndMemberInPath::load_member_from_path(family, member_id) + .await + .map_err(|e| { + log::error!("Failed to extract member ID from URL! {}", e); + actix_web::error::ErrorNotFound("Could not fetch member information!") + }) + }) + } +} diff --git a/geneit_backend/src/extractors/mod.rs b/geneit_backend/src/extractors/mod.rs index 75c1468..11850c0 100644 --- a/geneit_backend/src/extractors/mod.rs +++ b/geneit_backend/src/extractors/mod.rs @@ -1 +1,2 @@ pub mod family_extractor; +pub mod member_extractor; diff --git a/geneit_backend/src/main.rs b/geneit_backend/src/main.rs index e0b6cf0..2449cae 100644 --- a/geneit_backend/src/main.rs +++ b/geneit_backend/src/main.rs @@ -127,6 +127,10 @@ async fn main() -> std::io::Result<()> { "/family/{id}/member/create", web::post().to(members_controller::create), ) + .route( + "/family/{id}/member/{member_id}", + web::get().to(members_controller::get_single), + ) }) .bind(AppConfig::get().listen_address.as_str())? .run() diff --git a/geneit_backend/src/models.rs b/geneit_backend/src/models.rs index b069396..7532a90 100644 --- a/geneit_backend/src/models.rs +++ b/geneit_backend/src/models.rs @@ -53,7 +53,7 @@ pub struct NewUser<'a> { } /// Family ID holder -#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] +#[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize, PartialEq, Eq)] pub struct FamilyID(pub i32); #[derive(Queryable, Debug, serde::Serialize)]