Can create a family member
This commit is contained in:
		
							
								
								
									
										242
									
								
								geneit_backend/src/controllers/members_controller.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										242
									
								
								geneit_backend/src/controllers/members_controller.rs
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,242 @@
 | 
			
		||||
use crate::constants::{SizeConstraint, StaticConstraints};
 | 
			
		||||
use crate::controllers::HttpResult;
 | 
			
		||||
use crate::extractors::family_extractor::FamilyInPath;
 | 
			
		||||
use crate::models::{Member, MemberID, Sex};
 | 
			
		||||
use crate::services::members_service;
 | 
			
		||||
use crate::utils::countries_utils;
 | 
			
		||||
use actix_web::{web, HttpResponse};
 | 
			
		||||
 | 
			
		||||
serde_with::with_prefix!(prefix_birth "birth_");
 | 
			
		||||
serde_with::with_prefix!(prefix_death "death_");
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
pub struct RequestDate {
 | 
			
		||||
    pub year: Option<i16>,
 | 
			
		||||
    pub month: Option<i16>,
 | 
			
		||||
    pub day: Option<i16>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl RequestDate {
 | 
			
		||||
    pub fn check(&self) -> bool {
 | 
			
		||||
        let c = StaticConstraints::default();
 | 
			
		||||
 | 
			
		||||
        self.year.map(|y| c.date_year.validate(y)).unwrap_or(true)
 | 
			
		||||
            && self.month.map(|y| c.date_month.validate(y)).unwrap_or(true)
 | 
			
		||||
            && self.day.map(|y| c.date_day.validate(y)).unwrap_or(true)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(serde::Deserialize)]
 | 
			
		||||
pub struct MemberRequest {
 | 
			
		||||
    first_name: Option<String>,
 | 
			
		||||
    last_name: Option<String>,
 | 
			
		||||
    birth_last_name: Option<String>,
 | 
			
		||||
    email: Option<String>,
 | 
			
		||||
    phone: Option<String>,
 | 
			
		||||
    address: Option<String>,
 | 
			
		||||
    city: Option<String>,
 | 
			
		||||
    postal_code: Option<String>,
 | 
			
		||||
    country: Option<String>,
 | 
			
		||||
    sex: Option<Sex>,
 | 
			
		||||
    mother: Option<MemberID>,
 | 
			
		||||
    father: Option<MemberID>,
 | 
			
		||||
    #[serde(flatten, with = "prefix_birth")]
 | 
			
		||||
    birth: Option<RequestDate>,
 | 
			
		||||
    #[serde(flatten, with = "prefix_death")]
 | 
			
		||||
    death: Option<RequestDate>,
 | 
			
		||||
    note: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(thiserror::Error, Debug)]
 | 
			
		||||
enum MemberControllerErr {
 | 
			
		||||
    #[error("Malformed first name!")]
 | 
			
		||||
    MalformedFirstname,
 | 
			
		||||
    #[error("Malformed last name!")]
 | 
			
		||||
    MalformedLastname,
 | 
			
		||||
    #[error("Malformed birth last name!")]
 | 
			
		||||
    MalformedBirthLastname,
 | 
			
		||||
    #[error("Malformed email address!")]
 | 
			
		||||
    MalformedEmailAddress,
 | 
			
		||||
    #[error("Invalid email address!")]
 | 
			
		||||
    InvalidEmailAddress,
 | 
			
		||||
    #[error("Malformed phone number!")]
 | 
			
		||||
    MalformedPhoneNumber,
 | 
			
		||||
    #[error("Malformed address!")]
 | 
			
		||||
    MalformedAddress,
 | 
			
		||||
    #[error("Malformed city!")]
 | 
			
		||||
    MalformedCity,
 | 
			
		||||
    #[error("Malformed postal code!")]
 | 
			
		||||
    MalformedPostalCode,
 | 
			
		||||
    #[error("Malformed country!")]
 | 
			
		||||
    MalformedCountry,
 | 
			
		||||
    #[error("Invalid country code!")]
 | 
			
		||||
    InvalidCountryCode,
 | 
			
		||||
    #[error("Malformed date of birth!")]
 | 
			
		||||
    MalformedDateOfBirth,
 | 
			
		||||
    #[error("Malformed date of death!")]
 | 
			
		||||
    MalformedDateOfDeath,
 | 
			
		||||
    #[error("Malformed note!")]
 | 
			
		||||
    MalformedNote,
 | 
			
		||||
    #[error("Mother does not exists!")]
 | 
			
		||||
    MotherNotExisting,
 | 
			
		||||
    #[error("Father does not exists!")]
 | 
			
		||||
    FatherNotExisting,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn check_opt_str_val(
 | 
			
		||||
    val: &Option<String>,
 | 
			
		||||
    c: SizeConstraint,
 | 
			
		||||
    err: MemberControllerErr,
 | 
			
		||||
) -> anyhow::Result<()> {
 | 
			
		||||
    if let Some(v) = val {
 | 
			
		||||
        if !c.validate(v) {
 | 
			
		||||
            return Err(err.into());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    Ok(())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MemberRequest {
 | 
			
		||||
    pub async fn to_member(self, member: &mut Member) -> anyhow::Result<()> {
 | 
			
		||||
        let c = StaticConstraints::default();
 | 
			
		||||
        check_opt_str_val(
 | 
			
		||||
            &self.first_name,
 | 
			
		||||
            c.member_first_name,
 | 
			
		||||
            MemberControllerErr::MalformedFirstname,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        check_opt_str_val(
 | 
			
		||||
            &self.last_name,
 | 
			
		||||
            c.member_last_name,
 | 
			
		||||
            MemberControllerErr::MalformedLastname,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        check_opt_str_val(
 | 
			
		||||
            &self.birth_last_name,
 | 
			
		||||
            c.member_birth_last_name,
 | 
			
		||||
            MemberControllerErr::MalformedBirthLastname,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        check_opt_str_val(
 | 
			
		||||
            &self.email,
 | 
			
		||||
            c.member_email,
 | 
			
		||||
            MemberControllerErr::MalformedEmailAddress,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        if let Some(mail) = &self.email {
 | 
			
		||||
            if !mailchecker::is_valid(mail) {
 | 
			
		||||
                return Err(MemberControllerErr::InvalidEmailAddress.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        check_opt_str_val(
 | 
			
		||||
            &self.phone,
 | 
			
		||||
            c.member_phone,
 | 
			
		||||
            MemberControllerErr::MalformedPhoneNumber,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        check_opt_str_val(
 | 
			
		||||
            &self.address,
 | 
			
		||||
            c.member_address,
 | 
			
		||||
            MemberControllerErr::MalformedAddress,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        check_opt_str_val(
 | 
			
		||||
            &self.city,
 | 
			
		||||
            c.member_city,
 | 
			
		||||
            MemberControllerErr::MalformedCity,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        check_opt_str_val(
 | 
			
		||||
            &self.postal_code,
 | 
			
		||||
            c.member_postal_code,
 | 
			
		||||
            MemberControllerErr::MalformedPostalCode,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        check_opt_str_val(
 | 
			
		||||
            &self.country,
 | 
			
		||||
            c.member_country,
 | 
			
		||||
            MemberControllerErr::MalformedCountry,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        if let Some(c) = &self.country {
 | 
			
		||||
            if !countries_utils::is_code_valid(c) {
 | 
			
		||||
                return Err(MemberControllerErr::InvalidCountryCode.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(d) = &self.birth {
 | 
			
		||||
            if !d.check() {
 | 
			
		||||
                return Err(MemberControllerErr::MalformedDateOfBirth.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(d) = &self.death {
 | 
			
		||||
            if !d.check() {
 | 
			
		||||
                return Err(MemberControllerErr::MalformedDateOfDeath.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        check_opt_str_val(
 | 
			
		||||
            &self.note,
 | 
			
		||||
            c.member_note,
 | 
			
		||||
            MemberControllerErr::MalformedNote,
 | 
			
		||||
        )?;
 | 
			
		||||
 | 
			
		||||
        if let Some(mother) = self.mother {
 | 
			
		||||
            if !members_service::exists(member.family_id(), mother).await? {
 | 
			
		||||
                return Err(MemberControllerErr::MotherNotExisting.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if let Some(father) = self.father {
 | 
			
		||||
            if !members_service::exists(member.family_id(), father).await? {
 | 
			
		||||
                return Err(MemberControllerErr::FatherNotExisting.into());
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        member.first_name = self.first_name;
 | 
			
		||||
        member.last_name = self.last_name;
 | 
			
		||||
        member.birth_last_name = self.birth_last_name;
 | 
			
		||||
        member.email = self.email;
 | 
			
		||||
        member.phone = self.phone;
 | 
			
		||||
        member.address = self.address;
 | 
			
		||||
        member.city = self.city;
 | 
			
		||||
        member.postal_code = self.postal_code;
 | 
			
		||||
        member.country = self.country;
 | 
			
		||||
        member.set_sex(self.sex);
 | 
			
		||||
        member.set_mother(self.mother);
 | 
			
		||||
        member.set_father(self.father);
 | 
			
		||||
 | 
			
		||||
        member.birth_year = self.birth.as_ref().map(|m| m.year).unwrap_or_default();
 | 
			
		||||
        member.birth_month = self.birth.as_ref().map(|m| m.month).unwrap_or_default();
 | 
			
		||||
        member.birth_day = self.birth.as_ref().map(|m| m.day).unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
        member.death_year = self.death.as_ref().map(|m| m.year).unwrap_or_default();
 | 
			
		||||
        member.death_month = self.death.as_ref().map(|m| m.month).unwrap_or_default();
 | 
			
		||||
        member.death_day = self.death.as_ref().map(|m| m.day).unwrap_or_default();
 | 
			
		||||
 | 
			
		||||
        member.note = self.note;
 | 
			
		||||
 | 
			
		||||
        Ok(())
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Create a new family member
 | 
			
		||||
pub async fn create(f: FamilyInPath, req: web::Json<MemberRequest>) -> HttpResult {
 | 
			
		||||
    let mut member = members_service::create(f.family_id()).await?;
 | 
			
		||||
 | 
			
		||||
    if let Err(e) = req.0.to_member(&mut member).await {
 | 
			
		||||
        log::error!("Failed to apply member information! {e}");
 | 
			
		||||
        members_service::delete(&member).await?;
 | 
			
		||||
        return Ok(HttpResponse::BadRequest().body(e.to_string()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if let Err(e) = members_service::update(&mut member).await {
 | 
			
		||||
        log::error!("Failed to update member information! {e}");
 | 
			
		||||
        members_service::delete(&member).await?;
 | 
			
		||||
        return Ok(HttpResponse::InternalServerError().finish());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    Ok(HttpResponse::Ok().json(member))
 | 
			
		||||
}
 | 
			
		||||
@@ -6,6 +6,7 @@ use std::fmt::{Debug, Display, Formatter};
 | 
			
		||||
 | 
			
		||||
pub mod auth_controller;
 | 
			
		||||
pub mod families_controller;
 | 
			
		||||
pub mod members_controller;
 | 
			
		||||
pub mod server_controller;
 | 
			
		||||
pub mod users_controller;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
use crate::app_config::{AppConfig, OIDCProvider};
 | 
			
		||||
use crate::constants::StaticConstraints;
 | 
			
		||||
use crate::utils::countries_utils;
 | 
			
		||||
use crate::utils::countries_utils::CountryCode;
 | 
			
		||||
use actix_web::{HttpResponse, Responder};
 | 
			
		||||
 | 
			
		||||
/// Default hello route
 | 
			
		||||
@@ -12,6 +14,7 @@ struct ServerConfig<'a> {
 | 
			
		||||
    constraints: StaticConstraints,
 | 
			
		||||
    mail: &'static str,
 | 
			
		||||
    oidc_providers: Vec<OIDCProvider<'a>>,
 | 
			
		||||
    countries: Vec<CountryCode>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Default for ServerConfig<'_> {
 | 
			
		||||
@@ -20,6 +23,7 @@ impl Default for ServerConfig<'_> {
 | 
			
		||||
            mail: AppConfig::get().mail_sender.as_str(),
 | 
			
		||||
            constraints: StaticConstraints::default(),
 | 
			
		||||
            oidc_providers: AppConfig::get().openid_providers(),
 | 
			
		||||
            countries: countries_utils::get_list(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user