diff --git a/api/src/routes/admin.rs b/api/src/routes/admin.rs index 15f6823..2479ed3 100644 --- a/api/src/routes/admin.rs +++ b/api/src/routes/admin.rs @@ -2,7 +2,7 @@ use std::net::{SocketAddr, IpAddr, Ipv4Addr}; use portfolio_core::{ crypto::random_8_char_string, - services::{admin_service::AdminService, candidate_service::CandidateService, application_service::ApplicationService, portfolio_service::PortfolioService}, responses::{BaseCandidateResponse, CreateCandidateResponse}, candidate_details::ApplicationDetails, sea_orm::prelude::Uuid, + services::{admin_service::AdminService, candidate_service::CandidateService, application_service::ApplicationService, portfolio_service::PortfolioService}, models::candidate::{BaseCandidateResponse, CreateCandidateResponse, ApplicationDetails}, sea_orm::prelude::Uuid, }; use requests::{AdminLoginRequest, RegisterRequest}; use rocket::http::{Cookie, Status, CookieJar}; @@ -192,7 +192,7 @@ pub async fn get_candidate_portfolio( #[cfg(test)] pub mod tests { - use portfolio_core::responses::CreateCandidateResponse; + use portfolio_core::models::candidate::CreateCandidateResponse; use rocket::{local::blocking::Client, http::{Cookie, Status}}; use crate::test::tests::{test_client, ADMIN_PASSWORD, ADMIN_ID}; diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index 93b4d6f..f87854b 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -1,6 +1,6 @@ use std::net::{IpAddr, Ipv4Addr, SocketAddr}; -use portfolio_core::candidate_details::ApplicationDetails; +use portfolio_core::models::candidate::ApplicationDetails; use portfolio_core::sea_orm::prelude::Uuid; use portfolio_core::services::application_service::ApplicationService; use portfolio_core::services::candidate_service::CandidateService; @@ -264,7 +264,7 @@ pub async fn download_portfolio(session: CandidateAuth) -> Result, Custo #[cfg(test)] mod tests { - use portfolio_core::{candidate_details::ApplicationDetails, crypto, sea_orm::prelude::Uuid}; + use portfolio_core::{models::candidate::ApplicationDetails, crypto, sea_orm::prelude::Uuid}; use rocket::{ http::{Cookie, Status}, local::blocking::Client, diff --git a/core/src/database/mutation/candidate.rs b/core/src/database/mutation/candidate.rs index dbc2f55..04f90e8 100644 --- a/core/src/database/mutation/candidate.rs +++ b/core/src/database/mutation/candidate.rs @@ -1,4 +1,4 @@ -use crate::{candidate_details::EncryptedApplicationDetails, Mutation}; +use crate::{Mutation, models::candidate_details::EncryptedApplicationDetails}; use ::entity::candidate::{self}; use sea_orm::*; @@ -67,8 +67,8 @@ impl Mutation { #[cfg(test)] mod tests { - use crate::candidate_details::tests::APPLICATION_DETAILS; - use crate::candidate_details::{EncryptedApplicationDetails}; + use crate::models::candidate_details::EncryptedApplicationDetails; + use crate::models::candidate_details::tests::APPLICATION_DETAILS; use crate::utils::db::get_memory_sqlite_connection; use crate::{Mutation, Query}; diff --git a/core/src/database/mutation/parent.rs b/core/src/database/mutation/parent.rs index c31298c..0001700 100644 --- a/core/src/database/mutation/parent.rs +++ b/core/src/database/mutation/parent.rs @@ -1,4 +1,4 @@ -use crate::{candidate_details::EncryptedApplicationDetails, Mutation}; +use crate::{Mutation, models::candidate_details::EncryptedApplicationDetails}; use ::entity::parent::{self, Model}; use sea_orm::*; @@ -34,8 +34,8 @@ impl Mutation { #[cfg(test)] mod tests { - use crate::candidate_details::tests::APPLICATION_DETAILS; - use crate::candidate_details::{EncryptedApplicationDetails}; + use crate::models::candidate_details::EncryptedApplicationDetails; + use crate::models::candidate_details::tests::APPLICATION_DETAILS; use crate::utils::db::get_memory_sqlite_connection; use crate::{Mutation, Query}; diff --git a/core/src/database/query/candidate.rs b/core/src/database/query/candidate.rs index ae24441..f41cd0b 100644 --- a/core/src/database/query/candidate.rs +++ b/core/src/database/query/candidate.rs @@ -1,45 +1,11 @@ use sea_orm::*; -use serde::Serialize; use ::entity::{candidate, candidate::Entity as Candidate, parent}; -use crate::Query; +use crate::{Query, models::candidate::CandidateWithParent}; pub const PAGE_SIZE: u64 = 20; -#[derive(FromQueryResult, Serialize)] -pub struct CandidateParentResult { - pub application: i32, - pub name: Option, - pub surname: Option, - pub study: Option, - pub citizenship: Option, - - pub parent_name: Option, - pub parent_surname: Option, -} - -#[derive(FromQueryResult, Serialize, Default)] -pub struct CandidateWithParent { // TODO: use this instead of (Candidate, Parent)??? - pub application: i32, - pub name: Option, - pub surname: Option, - pub birthplace: Option, - pub birthdate: Option, - pub address: Option, - pub telephone: Option, - pub citizenship: Option, - pub email: Option, - pub sex: Option, - pub study: Option, - pub personal_identification_number: Option, - - pub parent_name: Option, - pub parent_surname: Option, - pub parent_telephone: Option, - pub parent_email: Option, -} - #[derive(FromQueryResult)] pub struct ApplicationId { application: i32, @@ -51,6 +17,18 @@ impl ApplicationId { } } +#[derive(FromQueryResult)] +pub struct CandidateParentResult { + pub application: i32, + pub name: Option, + pub surname: Option, + pub study: Option, + pub citizenship: Option, + + pub parent_name: Option, + pub parent_surname: Option, +} + impl Query { pub async fn find_candidate_by_id( db: &DbConn, diff --git a/core/src/lib.rs b/core/src/lib.rs index e58447a..c429793 100644 --- a/core/src/lib.rs +++ b/core/src/lib.rs @@ -7,7 +7,6 @@ pub mod database; pub mod crypto; pub mod services; pub mod error; -pub mod candidate_details; pub mod responses; pub mod utils; - +pub mod models; diff --git a/core/src/models/candidate.rs b/core/src/models/candidate.rs new file mode 100644 index 0000000..0f7688c --- /dev/null +++ b/core/src/models/candidate.rs @@ -0,0 +1,98 @@ +use chrono::NaiveDate; +use sea_orm::FromQueryResult; +use serde::{Serialize, Deserialize}; + +use crate::{error::ServiceError}; + +use super::candidate_details::decrypt_if_exists; + +/// Create candidate (admin endpoint) +/// Password change (admin endpoint) +#[derive(Debug, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct CreateCandidateResponse { + pub application_id: i32, + pub personal_id_number: String, + pub password: String, +} + +/// List candidates (admin endpoint) +#[derive(Debug, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct BaseCandidateResponse { + pub application_id: i32, + pub name: String, + pub surname: String, + pub study: String, + pub submitted: bool, +} + +/// Candidate details (admin and candidate endpoints) +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] +#[serde(rename_all = "camelCase")] +pub struct ApplicationDetails { + // Candidate + pub name: String, + pub surname: String, + pub birthplace: String, + pub birthdate: NaiveDate, // TODO: User NaiveDate or String? + pub address: String, + pub telephone: String, + pub citizenship: String, + pub email: String, + pub sex: String, + pub study: String, + pub personal_id_number: String, + // Parent + pub parent_name: String, + pub parent_surname: String, + pub parent_telephone: String, + pub parent_email: String, +} + +/// CSV export (admin endpoint) +#[derive(FromQueryResult, Serialize, Default)] +#[serde(rename_all = "camelCase")] +pub struct CandidateWithParent { // TODO: use this instead of (Candidate, Parent)??? + pub application: i32, + pub name: Option, + pub surname: Option, + pub birthplace: Option, + pub birthdate: Option, + pub address: Option, + pub telephone: Option, + pub citizenship: Option, + pub email: Option, + pub sex: Option, + pub study: Option, + pub personal_identification_number: Option, + + pub parent_name: Option, + pub parent_surname: Option, + pub parent_telephone: Option, + pub parent_email: Option, +} + +impl BaseCandidateResponse { + pub async fn from_encrypted( + private_key: &String, + application_id: i32, + name_opt: Option, + surname_opt: Option, + study_opt: Option, + submitted: bool, + ) -> Result { + let name = decrypt_if_exists(private_key, name_opt).await?; + let surname = decrypt_if_exists(private_key, surname_opt).await?; + Ok( + Self { + name, + application_id, + surname, + study: study_opt.unwrap_or("".to_string()), + submitted, + } + ) + } + +} \ No newline at end of file diff --git a/core/src/candidate_details.rs b/core/src/models/candidate_details.rs similarity index 95% rename from core/src/candidate_details.rs rename to core/src/models/candidate_details.rs index 0f7db19..f0de1fb 100644 --- a/core/src/candidate_details.rs +++ b/core/src/models/candidate_details.rs @@ -1,15 +1,36 @@ use chrono::NaiveDate; -use serde::{Deserialize, Serialize}; use entity::{candidate, parent}; -use crate::{crypto, database::query::candidate::CandidateWithParent, error::ServiceError}; +use crate::{crypto, models::candidate::{CandidateWithParent, ApplicationDetails}, error::ServiceError}; pub const NAIVE_DATE_FMT: &str = "%Y-%m-%d"; #[derive(Clone)] pub struct EncryptedString(String); +#[derive(Clone)] +pub struct EncryptedApplicationDetails { + // Candidate + pub name: EncryptedString, + pub surname: EncryptedString, + pub birthplace: EncryptedString, + pub birthdate: EncryptedString, + pub address: EncryptedString, + pub telephone: EncryptedString, + pub citizenship: EncryptedString, + pub email: EncryptedString, + pub sex: EncryptedString, + pub personal_id_number: EncryptedString, + pub study: String, + + // Parent + pub parent_name: EncryptedString, + pub parent_surname: EncryptedString, + pub parent_telephone: EncryptedString, + pub parent_email: EncryptedString, +} + impl EncryptedString { pub async fn new(s: &str, recipients: &Vec) -> Result { let recipients = recipients.iter().map(|s| &**s).collect(); @@ -65,28 +86,6 @@ impl TryFrom> for EncryptedString { } } -#[derive(Clone)] -pub struct EncryptedApplicationDetails { - // Candidate - pub name: EncryptedString, - pub surname: EncryptedString, - pub birthplace: EncryptedString, - pub birthdate: EncryptedString, - pub address: EncryptedString, - pub telephone: EncryptedString, - pub citizenship: EncryptedString, - pub email: EncryptedString, - pub sex: EncryptedString, - pub personal_id_number: EncryptedString, - pub study: String, - - // Parent - pub parent_name: EncryptedString, - pub parent_surname: EncryptedString, - pub parent_telephone: EncryptedString, - pub parent_email: EncryptedString, -} - impl EncryptedApplicationDetails { pub async fn new( form: &ApplicationDetails, @@ -224,27 +223,15 @@ impl TryFrom for EncryptedApplicationDetails { } } - - -#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] -pub struct ApplicationDetails { - // Candidate - pub name: String, - pub surname: String, - pub birthplace: String, - pub birthdate: NaiveDate, // TODO: User NaiveDate or String? - pub address: String, - pub telephone: String, - pub citizenship: String, - pub email: String, - pub sex: String, - pub study: String, - pub personal_id_number: String, - // Parent - pub parent_name: String, - pub parent_surname: String, - pub parent_telephone: String, - pub parent_email: String, +// TODO: use this more??? +pub async fn decrypt_if_exists( + private_key: &String, + encrypted_string: Option, +) -> Result { + match EncryptedString::try_from(encrypted_string) { + Ok(encrypted_string) => Ok(encrypted_string.decrypt(private_key).await?), + Err(_) => Ok(String::from("")), + } } #[cfg(test)] diff --git a/core/src/models/mod.rs b/core/src/models/mod.rs new file mode 100644 index 0000000..6cd26ab --- /dev/null +++ b/core/src/models/mod.rs @@ -0,0 +1,2 @@ +pub mod candidate_details; +pub mod candidate; \ No newline at end of file diff --git a/core/src/responses.rs b/core/src/responses.rs index a1ccf56..e69de29 100644 --- a/core/src/responses.rs +++ b/core/src/responses.rs @@ -1,53 +0,0 @@ -use serde::{Serialize, Deserialize}; - -use crate::{candidate_details::EncryptedString, error::ServiceError}; - -#[derive(Debug, Serialize, Deserialize)] -pub struct CreateCandidateResponse { - pub application_id: i32, - pub personal_id_number: String, - pub password: String, -} - -#[derive(Debug, Serialize)] -pub struct BaseCandidateResponse { - pub application_id: i32, - pub name: String, - pub surname: String, - pub study: String, - pub submitted: bool, -} - -impl BaseCandidateResponse { - pub async fn from_encrypted( - private_key: &String, - application_id: i32, - name_opt: Option, - surname_opt: Option, - study_opt: Option, - submitted: bool, - ) -> Result { - let name = decrypt_if_exists(private_key, name_opt).await?; - let surname = decrypt_if_exists(private_key, surname_opt).await?; - Ok( - Self { - name, - application_id, - surname, - study: study_opt.unwrap_or("".to_string()), - submitted, - } - ) - } - -} - -async fn decrypt_if_exists( - private_key: &String, - encrypted_string: Option, -) -> Result { - match EncryptedString::try_from(encrypted_string) { - Ok(encrypted_string) => Ok(encrypted_string.decrypt(private_key).await?), - Err(_) => Ok(String::from("")), - } -} \ No newline at end of file diff --git a/core/src/services/application_service.rs b/core/src/services/application_service.rs index e245a4b..d8ad37f 100644 --- a/core/src/services/application_service.rs +++ b/core/src/services/application_service.rs @@ -1,7 +1,7 @@ use entity::{candidate, parent}; use sea_orm::DbConn; -use crate::{error::ServiceError, candidate_details::{ApplicationDetails, EncryptedApplicationDetails}, Query, utils::db::get_recipients}; +use crate::{error::ServiceError, Query, utils::db::get_recipients, models::candidate_details::{EncryptedApplicationDetails}, models::candidate::ApplicationDetails}; use super::{parent_service::ParentService, candidate_service::CandidateService}; diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 34cd9b5..cdd7c3e 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -2,10 +2,10 @@ use entity::candidate; use sea_orm::{prelude::Uuid, DbConn}; use crate::{ - candidate_details::{EncryptedApplicationDetails, EncryptedString}, + models::candidate_details::{EncryptedApplicationDetails, EncryptedString}, crypto::{self, hash_password}, error::ServiceError, - Mutation, Query, responses::{BaseCandidateResponse, CreateCandidateResponse}, utils::db::get_recipients, + Mutation, Query, models::candidate::{BaseCandidateResponse, CreateCandidateResponse}, utils::db::get_recipients, }; use super::{session_service::{AdminUser, SessionService}, application_service::ApplicationService, portfolio_service::PortfolioService}; @@ -254,11 +254,11 @@ impl CandidateService { pub mod tests { use sea_orm::{DbConn}; - use crate::candidate_details::tests::assert_all_application_details; + use crate::models::candidate_details::tests::assert_all_application_details; use crate::utils::db::get_memory_sqlite_connection; use crate::{crypto, services::candidate_service::CandidateService, Mutation}; - use super::EncryptedApplicationDetails; + use crate::models::candidate_details::EncryptedApplicationDetails; use entity::{candidate, parent, admin}; use crate::services::application_service::ApplicationService; @@ -374,7 +374,7 @@ pub mod tests { #[cfg(test)] pub async fn put_user_data(db: &DbConn) -> (candidate::Model, parent::Model) { - use crate::candidate_details::tests::APPLICATION_DETAILS; + use crate::models::candidate_details::tests::APPLICATION_DETAILS; let plain_text_password = "test".to_string(); let (candidate, _parent) = ApplicationService::create_candidate_with_parent( diff --git a/core/src/services/parent_service.rs b/core/src/services/parent_service.rs index 1990be4..3372201 100644 --- a/core/src/services/parent_service.rs +++ b/core/src/services/parent_service.rs @@ -1,7 +1,7 @@ use entity::{parent}; use sea_orm::DbConn; -use crate::{error::ServiceError, Mutation, candidate_details::EncryptedApplicationDetails}; +use crate::{error::ServiceError, Mutation, models::candidate_details::EncryptedApplicationDetails}; pub struct ParentService; diff --git a/core/src/utils/csv.rs b/core/src/utils/csv.rs index 3ee2e25..4faf415 100644 --- a/core/src/utils/csv.rs +++ b/core/src/utils/csv.rs @@ -1,5 +1,5 @@ use sea_orm::{DbConn}; -use crate::{error::ServiceError, candidate_details::{EncryptedApplicationDetails, ApplicationDetails}, Query, database::query::candidate::CandidateWithParent}; +use crate::{error::ServiceError, models::candidate_details::{EncryptedApplicationDetails}, Query, models::candidate::{CandidateWithParent, ApplicationDetails}}; type Row = CandidateWithParent;