diff --git a/geneit_backend/src/controllers/families_controller.rs b/geneit_backend/src/controllers/families_controller.rs index f658661..b275898 100644 --- a/geneit_backend/src/controllers/families_controller.rs +++ b/geneit_backend/src/controllers/families_controller.rs @@ -1,5 +1,6 @@ use crate::constants::StaticConstraints; use crate::controllers::HttpResult; +use crate::extractors::family_extractor::FamilyInPath; use crate::services::login_token_service::LoginToken; use crate::services::rate_limiter_service::RatedAction; use crate::services::{families_service, rate_limiter_service}; @@ -66,5 +67,14 @@ pub async fn join( /// Get the list of families of the user pub async fn list(token: LoginToken) -> HttpResult { - Ok(HttpResponse::Ok().json(families_service::get_user_memberships(token.user_id).await?)) + Ok( + HttpResponse::Ok() + .json(families_service::get_user_family_memberships(token.user_id).await?), + ) +} + +/// Get the information of a single family +pub async fn single_info(f: FamilyInPath) -> HttpResult { + Ok(HttpResponse::Ok() + .json(families_service::get_family_membership(f.family_id(), f.user_id()).await?)) } diff --git a/geneit_backend/src/extractors/family_extractor.rs b/geneit_backend/src/extractors/family_extractor.rs new file mode 100644 index 0000000..ffda0d6 --- /dev/null +++ b/geneit_backend/src/extractors/family_extractor.rs @@ -0,0 +1,56 @@ +use crate::models::{FamilyID, Membership, UserID}; +use crate::services::families_service; +use crate::services::login_token_service::LoginToken; +use actix_web::dev::Payload; +use actix_web::{FromRequest, HttpRequest}; +use serde::Deserialize; + +#[derive(Debug)] +pub struct FamilyInPath(Membership); + +impl FamilyInPath { + async fn load_family_from_path(t: &LoginToken, id: FamilyID) -> anyhow::Result { + Ok(Self(families_service::get_membership(id, t.user_id).await?)) + } + + pub fn user_id(&self) -> UserID { + self.0.user_id() + } + + pub fn family_id(&self) -> FamilyID { + self.0.family_id() + } + + pub fn is_admin(&self) -> bool { + self.0.is_admin + } +} + +#[derive(Deserialize)] +struct FamilyIdInPath { + id: FamilyID, +} + +impl FromRequest for FamilyInPath { + 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 token = LoginToken::extract(&req).await?; + + let family_id = + actix_web::web::Path::::from_request(&req, &mut Payload::None) + .await? + .id; + + FamilyInPath::load_family_from_path(&token, family_id) + .await + .map_err(|e| { + log::error!("Failed to extract family ID from URL! {}", e); + actix_web::error::ErrorNotFound("Could not fetch family information!") + }) + }) + } +} diff --git a/geneit_backend/src/extractors/mod.rs b/geneit_backend/src/extractors/mod.rs new file mode 100644 index 0000000..75c1468 --- /dev/null +++ b/geneit_backend/src/extractors/mod.rs @@ -0,0 +1 @@ +pub mod family_extractor; diff --git a/geneit_backend/src/lib.rs b/geneit_backend/src/lib.rs index b244aba..a9b17d0 100644 --- a/geneit_backend/src/lib.rs +++ b/geneit_backend/src/lib.rs @@ -2,6 +2,7 @@ pub mod app_config; pub mod connections; pub mod constants; pub mod controllers; +pub mod extractors; pub mod services; pub mod utils; diff --git a/geneit_backend/src/main.rs b/geneit_backend/src/main.rs index f09296c..92d2e73 100644 --- a/geneit_backend/src/main.rs +++ b/geneit_backend/src/main.rs @@ -93,6 +93,10 @@ async fn main() -> std::io::Result<()> { ) .route("/family/join", web::post().to(families_controller::join)) .route("/family/list", web::get().to(families_controller::list)) + .route( + "/family/{id}/info", + web::get().to(families_controller::single_info), + ) }) .bind(AppConfig::get().listen_address.as_str())? .run() diff --git a/geneit_backend/src/models.rs b/geneit_backend/src/models.rs index 39040e4..d623a58 100644 --- a/geneit_backend/src/models.rs +++ b/geneit_backend/src/models.rs @@ -80,10 +80,20 @@ pub struct NewFamily<'a> { #[derive(Queryable, Debug, serde::Serialize)] pub struct Membership { - user_id: i32, - family_id: i32, - time_create: i64, - is_admin: bool, + pub user_id: i32, + pub family_id: i32, + pub time_create: i64, + pub is_admin: bool, +} + +impl Membership { + pub fn user_id(&self) -> UserID { + UserID(self.user_id) + } + + pub fn family_id(&self) -> FamilyID { + FamilyID(self.family_id) + } } #[derive(Insertable)] diff --git a/geneit_backend/src/services/families_service.rs b/geneit_backend/src/services/families_service.rs index d6c8179..3fee365 100644 --- a/geneit_backend/src/services/families_service.rs +++ b/geneit_backend/src/services/families_service.rs @@ -70,7 +70,7 @@ pub async fn is_member(family_id: FamilyID, user_id: UserID) -> anyhow::Result anyhow::Result> { +pub async fn get_user_family_memberships(user_id: UserID) -> anyhow::Result> { db_connection::execute(|conn| { families_memberships::table .filter(families_memberships::dsl::user_id.eq(user_id.0)) @@ -78,6 +78,29 @@ pub async fn get_user_memberships(user_id: UserID) -> anyhow::Result anyhow::Result { + db_connection::execute(|conn| { + memberships::table + .filter(memberships::dsl::user_id.eq(user_id.0)) + .filter(memberships::dsl::family_id.eq(family_id.0)) + .get_result(conn) + }) +} + +/// Get information about a membership of a user, joined with family information +pub async fn get_family_membership( + family_id: FamilyID, + user_id: UserID, +) -> anyhow::Result { + db_connection::execute(|conn| { + families_memberships::table + .filter(families_memberships::dsl::user_id.eq(user_id.0)) + .filter(families_memberships::dsl::family_id.eq(family_id.0)) + .get_result(conn) + }) +} + /// Remove a membership to a family pub async fn remove_membership(_family_id: FamilyID, _user_id: UserID) { todo!()