From 8fd8ee66240319061a0b3d971f63ab8683531414 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Thu, 19 Jan 2023 18:35:56 +0100 Subject: [PATCH 1/9] feat!: school order selection *database* - firstSchool - secondSchool *CandidateDetails* School struct with 'name' & 'field' attributes --- api/src/routes/candidate.rs | 2 ++ core/src/database/mutation/candidate.rs | 2 ++ core/src/models/candidate.rs | 4 ++- core/src/models/candidate_details.rs | 24 ++++++++++++-- core/src/models/mod.rs | 3 +- core/src/models/school.rs | 31 +++++++++++++++++++ core/src/services/parent_service.rs | 4 ++- entity/src/candidate.rs | 2 ++ .../src/m20221024_121621_create_candidate.rs | 4 +++ 9 files changed, 71 insertions(+), 5 deletions(-) create mode 100644 core/src/models/school.rs diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index 49bfd34..0fab9db 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -319,6 +319,8 @@ mod tests { \"schoolName\": \"29988383\", \"healthInsurance\": \"000\", \"grades\": [], + \"firstSchool\": {\"name\": \"SSPÅ \", \"field\": \"KB\"}, + \"secondSchool\": {\"name\": \"SSPÅ \", \"field\": \"IT\"}, \"testLanguage\": \"CZ\" }, \"parents\": [ diff --git a/core/src/database/mutation/candidate.rs b/core/src/database/mutation/candidate.rs index ad7a961..aef582b 100644 --- a/core/src/database/mutation/candidate.rs +++ b/core/src/database/mutation/candidate.rs @@ -54,6 +54,8 @@ impl Mutation { candidate.school_name = Set(enc_candidate.school_name.map(|e| e.into())); candidate.health_insurance = Set(enc_candidate.health_insurance.map(|e| e.into())); candidate.grades_json = Set(enc_candidate.grades_json.map(|e| e.into())); + candidate.first_school = Set(enc_candidate.first_school.map(|e| e.into())); + candidate.second_school = Set(enc_candidate.second_school.map(|e| e.into())); candidate.test_language = Set(enc_candidate.test_language.map(|s| s)); candidate.encrypted_by_id = Set(Some(encrypted_by_id)); diff --git a/core/src/models/candidate.rs b/core/src/models/candidate.rs index b3b9ae4..920e387 100644 --- a/core/src/models/candidate.rs +++ b/core/src/models/candidate.rs @@ -6,7 +6,7 @@ use crate::{ error::ServiceError, }; -use super::{candidate_details::{EncryptedString, EncryptedCandidateDetails}, grade::GradeList}; +use super::{candidate_details::{EncryptedString, EncryptedCandidateDetails}, grade::GradeList, school::School}; pub enum FieldOfStudy { G, @@ -73,6 +73,8 @@ pub struct CandidateDetails { pub school_name: String, pub health_insurance: String, pub grades: GradeList, + pub first_school: School, + pub second_school: School, pub test_language: String, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] diff --git a/core/src/models/candidate_details.rs b/core/src/models/candidate_details.rs index 5400e46..9d243b6 100644 --- a/core/src/models/candidate_details.rs +++ b/core/src/models/candidate_details.rs @@ -5,7 +5,7 @@ use futures::future; use crate::{crypto, models::candidate::{ApplicationDetails}, error::ServiceError, utils::date::parse_naive_date_from_opt_str}; -use super::{candidate::{CandidateDetails, ParentDetails}, application::ApplicationRow, grade::GradeList}; +use super::{candidate::{CandidateDetails, ParentDetails}, application::ApplicationRow, grade::GradeList, school::School}; pub const NAIVE_DATE_FMT: &str = "%Y-%m-%d"; @@ -27,6 +27,8 @@ pub struct EncryptedCandidateDetails { pub school_name: Option, pub health_insurance: Option, pub grades_json: Option, + pub first_school: Option, + pub second_school: Option, pub test_language: Option, } @@ -121,6 +123,8 @@ impl EncryptedCandidateDetails { ) -> Result { let birthdate_str = form.birthdate.format(NAIVE_DATE_FMT).to_string(); let grades_str = form.grades.to_string(); + let (first_school_str, second_school_str) = + (form.first_school.to_string(), form.second_school.to_string()); let d = tokio::try_join!( EncryptedString::new_option(&form.name, recipients), EncryptedString::new_option(&form.surname, recipients), @@ -135,6 +139,8 @@ impl EncryptedCandidateDetails { EncryptedString::new_option(&form.school_name, recipients), EncryptedString::new_option(&form.health_insurance, recipients), EncryptedString::new_option(&grades_str, recipients), + EncryptedString::new_option(&first_school_str, recipients), + EncryptedString::new_option(&second_school_str, recipients), )?; Ok( @@ -152,6 +158,8 @@ impl EncryptedCandidateDetails { school_name: d.10, health_insurance: d.11, grades_json: d.12, + first_school: d.13, + second_school: d.14, test_language: Some(form.test_language.to_owned()), } ) @@ -172,8 +180,12 @@ impl EncryptedCandidateDetails { EncryptedString::decrypt_option(&self.school_name, priv_key), // 10 EncryptedString::decrypt_option(&self.health_insurance, priv_key), // 11 EncryptedString::decrypt_option(&self.grades_json, priv_key), // 12 + EncryptedString::decrypt_option(&self.first_school, priv_key), // 13 + EncryptedString::decrypt_option(&self.second_school, priv_key), // 14 )?; + println!("d: {:?}", d.12); + Ok(CandidateDetails { name: d.0.unwrap_or_default(), surname: d.1.unwrap_or_default(), @@ -188,6 +200,8 @@ impl EncryptedCandidateDetails { school_name: d.10.unwrap_or_default(), health_insurance: d.11.unwrap_or_default(), grades: GradeList::from_opt_str(d.12).unwrap_or_default(), + first_school: School::from_opt_str(d.13).unwrap_or_default(), + second_school: School::from_opt_str(d.14).unwrap_or_default(), test_language: self.test_language.to_owned().unwrap_or_default().to_string(), } ) @@ -224,6 +238,8 @@ impl From<&candidate::Model> for EncryptedCandidateDetails { school_name: EncryptedString::try_from(&candidate.school_name).ok(), health_insurance: EncryptedString::try_from(&candidate.health_insurance).ok(), grades_json: EncryptedString::try_from(&candidate.grades_json).ok(), + first_school: EncryptedString::try_from(&candidate.first_school).ok(), + second_school: EncryptedString::try_from(&candidate.second_school).ok(), test_language: candidate.test_language.to_owned(), } } @@ -362,6 +378,8 @@ impl TryFrom for EncryptedApplicationDetails { personal_id_number: EncryptedString::try_from(&cp.personal_identification_number).ok(), school_name: EncryptedString::try_from(&cp.school_name).ok(), health_insurance: EncryptedString::try_from(&cp.health_insurance).ok(), + first_school: None, // TODO + second_school: None, // TODO grades_json: None, // TODO test_language: None // TODO }, @@ -395,7 +413,7 @@ pub mod tests { use once_cell::sync::Lazy; use sea_orm::{DbConn, Set, ActiveModelTrait}; - use crate::{crypto, models::{candidate::{CandidateDetails, ParentDetails}, grade::GradeList}, utils::db::get_memory_sqlite_connection, services::candidate_service::tests::put_user_data}; + use crate::{crypto, models::{candidate::{CandidateDetails, ParentDetails}, grade::GradeList, school::School}, utils::db::get_memory_sqlite_connection, services::candidate_service::tests::put_user_data}; use super::{ApplicationDetails, EncryptedApplicationDetails, EncryptedString}; @@ -418,6 +436,8 @@ pub mod tests { school_name: "school_name".to_string(), health_insurance: "health_insurance".to_string(), grades: GradeList::from(vec![]), + first_school: School::from_opt_str(Some("{\"name\": \"SSPS\", \"field\": \"KB\"}".to_string())).unwrap(), + second_school: School::from_opt_str(Some("{\"name\": \"SSPS\", \"field\": \"IT\"}".to_string())).unwrap(), test_language: "test_language".to_string(), }, parents: vec![ParentDetails { diff --git a/core/src/models/mod.rs b/core/src/models/mod.rs index 08cc4f2..2f790c0 100644 --- a/core/src/models/mod.rs +++ b/core/src/models/mod.rs @@ -2,4 +2,5 @@ pub mod candidate_details; pub mod candidate; pub mod auth; pub mod application; -pub mod grade; \ No newline at end of file +pub mod grade; +pub mod school; \ No newline at end of file diff --git a/core/src/models/school.rs b/core/src/models/school.rs new file mode 100644 index 0000000..3a3ceed --- /dev/null +++ b/core/src/models/school.rs @@ -0,0 +1,31 @@ +use serde::{Serialize, Deserialize}; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub struct School { + name: String, + field: String, +} + +impl School { + pub fn from_opt_str(school: Option) -> Option { + println!("School: {:?}", school); + school.map( + |school| serde_json::from_str(&school).unwrap() // TODO: handle error + ) + } +} + +impl ToString for School { + fn to_string(&self) -> String { + serde_json::to_string(&self).unwrap() + } +} + +impl Default for School { + fn default() -> Self { + Self { + name: String::default(), + field: String::default(), + } + } +} \ No newline at end of file diff --git a/core/src/services/parent_service.rs b/core/src/services/parent_service.rs index 13d0299..a24636f 100644 --- a/core/src/services/parent_service.rs +++ b/core/src/services/parent_service.rs @@ -54,7 +54,7 @@ mod tests { use once_cell::sync::Lazy; - use crate::{utils::db::get_memory_sqlite_connection, models::{candidate::{ParentDetails, ApplicationDetails, CandidateDetails}, candidate_details::EncryptedApplicationDetails, grade::GradeList}, services::{candidate_service::{CandidateService, tests::put_user_data}, application_service::ApplicationService, parent_service::ParentService}, crypto}; + use crate::{utils::db::get_memory_sqlite_connection, models::{candidate::{ParentDetails, ApplicationDetails, CandidateDetails}, candidate_details::EncryptedApplicationDetails, grade::GradeList, school::School}, services::{candidate_service::{CandidateService, tests::put_user_data}, application_service::ApplicationService, parent_service::ParentService}, crypto}; pub static APPLICATION_DETAILS_TWO_PARENTS: Lazy> = Lazy::new(|| Mutex::new(ApplicationDetails { @@ -72,6 +72,8 @@ mod tests { school_name: "school_name".to_string(), health_insurance: "health_insurance".to_string(), grades: GradeList::from(vec![]), + first_school: School::from_opt_str(Some("{\"name\": \"SSPS\", \"field\": \"KB\"}".to_string())).unwrap(), + second_school: School::from_opt_str(Some("{\"name\": \"SSPS\", \"field\": \"IT\"}".to_string())).unwrap(), test_language: "test_language".to_string(), }, parents: vec![ParentDetails { diff --git a/entity/src/candidate.rs b/entity/src/candidate.rs index 7084efd..a48d4db 100644 --- a/entity/src/candidate.rs +++ b/entity/src/candidate.rs @@ -21,6 +21,8 @@ pub struct Model { pub school_name: Option, pub health_insurance: Option, pub grades_json: Option, + pub first_school: Option, + pub second_school: Option, pub test_language: Option, pub encrypted_by_id: Option, pub created_at: DateTime, diff --git a/migration/src/m20221024_121621_create_candidate.rs b/migration/src/m20221024_121621_create_candidate.rs index da7f7ce..4e46469 100644 --- a/migration/src/m20221024_121621_create_candidate.rs +++ b/migration/src/m20221024_121621_create_candidate.rs @@ -32,6 +32,8 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(Candidate::SchoolName).string()) .col(ColumnDef::new(Candidate::HealthInsurance).string()) .col(ColumnDef::new(Candidate::GradesJson).string()) + .col(ColumnDef::new(Candidate::FirstSchool).string()) + .col(ColumnDef::new(Candidate::SecondSchool).string()) .col(ColumnDef::new(Candidate::TestLanguage).string()) .col(ColumnDef::new(Candidate::EncryptedById).integer()) .col(ColumnDef::new(Candidate::CreatedAt).date_time().not_null()) @@ -66,6 +68,8 @@ pub enum Candidate { SchoolName, HealthInsurance, GradesJson, + FirstSchool, + SecondSchool, TestLanguage, EncryptedById, CreatedAt, From 9943b6aff71ac967efc66e321278930266d1ec58 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Thu, 19 Jan 2023 19:35:02 +0100 Subject: [PATCH 2/9] refactor: remove unused function --- core/src/models/candidate_details.rs | 48 +--------------------------- 1 file changed, 1 insertion(+), 47 deletions(-) diff --git a/core/src/models/candidate_details.rs b/core/src/models/candidate_details.rs index 9d243b6..ebe85fc 100644 --- a/core/src/models/candidate_details.rs +++ b/core/src/models/candidate_details.rs @@ -5,7 +5,7 @@ use futures::future; use crate::{crypto, models::candidate::{ApplicationDetails}, error::ServiceError, utils::date::parse_naive_date_from_opt_str}; -use super::{candidate::{CandidateDetails, ParentDetails}, application::ApplicationRow, grade::GradeList, school::School}; +use super::{candidate::{CandidateDetails, ParentDetails}, grade::GradeList, school::School}; pub const NAIVE_DATE_FMT: &str = "%Y-%m-%d"; @@ -358,52 +358,6 @@ impl From<(&candidate::Model, Vec)> for EncryptedApplicationDetai } } -impl TryFrom for EncryptedApplicationDetails { - type Error = ServiceError; - - fn try_from( - cp: ApplicationRow, - ) -> Result { - Ok(EncryptedApplicationDetails { - candidate: EncryptedCandidateDetails { - name: EncryptedString::try_from(&cp.name).ok(), - surname: EncryptedString::try_from(&cp.surname).ok(), - birthplace: EncryptedString::try_from(&cp.birthplace).ok(), - birthdate: EncryptedString::try_from(&cp.birthdate).ok(), - address: EncryptedString::try_from(&cp.address).ok(), - telephone: EncryptedString::try_from(&cp.telephone).ok(), - citizenship: EncryptedString::try_from(&cp.citizenship).ok(), - email: EncryptedString::try_from(&cp.email).ok(), - sex: EncryptedString::try_from(&cp.sex).ok(), - personal_id_number: EncryptedString::try_from(&cp.personal_identification_number).ok(), - school_name: EncryptedString::try_from(&cp.school_name).ok(), - health_insurance: EncryptedString::try_from(&cp.health_insurance).ok(), - first_school: None, // TODO - second_school: None, // TODO - grades_json: None, // TODO - test_language: None // TODO - }, - parents: vec![EncryptedParentDetails { - name: EncryptedString::try_from(&cp.parent_name).ok(), - surname: EncryptedString::try_from(&cp.parent_surname).ok(), - telephone: EncryptedString::try_from(&cp.parent_telephone).ok(), - email: EncryptedString::try_from(&cp.parent_email).ok(), - }] - - }) - } -} - -/* 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)] pub mod tests { use std::sync::Mutex; From 29ce46106f2feda5e1b8343ae5a11d7f5ab41564 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Thu, 19 Jan 2023 19:59:06 +0100 Subject: [PATCH 3/9] feat: csv export all data --- api/src/routes/admin.rs | 1 - core/src/models/application.rs | 9 +++++++++ core/src/models/candidate_details.rs | 2 -- core/src/models/grade.rs | 17 +++++++++++++++++ core/src/models/school.rs | 9 ++++++++- core/src/services/session_service.rs | 1 - core/src/utils/csv.rs | 12 +++++++++++- 7 files changed, 45 insertions(+), 6 deletions(-) diff --git a/api/src/routes/admin.rs b/api/src/routes/admin.rs index 0291e4b..1310511 100644 --- a/api/src/routes/admin.rs +++ b/api/src/routes/admin.rs @@ -256,7 +256,6 @@ pub mod tests { )) .dispatch(); - println!("{:?}", response); ( response.cookies().get("id").unwrap().to_owned(), response.cookies().get("key").unwrap().to_owned(), diff --git a/core/src/models/application.rs b/core/src/models/application.rs index 6cbb328..6224cf0 100644 --- a/core/src/models/application.rs +++ b/core/src/models/application.rs @@ -58,6 +58,15 @@ pub struct ApplicationRow { pub school_name: Option, pub health_insurance: Option, + pub diploma_1_8: String, + pub diploma_2_8: String, + pub diploma_1_9: String, + + pub first_school_name: Option, + pub first_school_field: Option, + pub second_school_name: Option, + pub second_school_field: Option, + pub parent_name: Option, pub parent_surname: Option, pub parent_telephone: Option, diff --git a/core/src/models/candidate_details.rs b/core/src/models/candidate_details.rs index ebe85fc..56b4ee7 100644 --- a/core/src/models/candidate_details.rs +++ b/core/src/models/candidate_details.rs @@ -184,8 +184,6 @@ impl EncryptedCandidateDetails { EncryptedString::decrypt_option(&self.second_school, priv_key), // 14 )?; - println!("d: {:?}", d.12); - Ok(CandidateDetails { name: d.0.unwrap_or_default(), surname: d.1.unwrap_or_default(), diff --git a/core/src/models/grade.rs b/core/src/models/grade.rs index d58a18a..acd72b7 100644 --- a/core/src/models/grade.rs +++ b/core/src/models/grade.rs @@ -16,6 +16,23 @@ impl GradeList { |grades| serde_json::from_str(&grades).unwrap() // TODO: handle error ) } + + pub fn group_by_semester(&self) -> (GradeList, GradeList, GradeList) { + let mut first_semester = GradeList::default(); + let mut second_semester = GradeList::default(); + let mut third_semester = GradeList::default(); + + for grade in &self.0 { + match grade.semester.as_str() { + "1/8" => first_semester.0.push(grade.clone()), + "2/8" => second_semester.0.push(grade.clone()), + "1/9" => third_semester.0.push(grade.clone()), + _ => panic!("Invalid semester"), + } + } + + (first_semester, second_semester, third_semester) + } } impl Default for GradeList { diff --git a/core/src/models/school.rs b/core/src/models/school.rs index 3a3ceed..27c18d6 100644 --- a/core/src/models/school.rs +++ b/core/src/models/school.rs @@ -8,11 +8,18 @@ pub struct School { impl School { pub fn from_opt_str(school: Option) -> Option { - println!("School: {:?}", school); school.map( |school| serde_json::from_str(&school).unwrap() // TODO: handle error ) } + + pub fn name(&self) -> &str { + &self.name + } + + pub fn field(&self) -> &str { + &self.field + } } impl ToString for School { diff --git a/core/src/services/session_service.rs b/core/src/services/session_service.rs index 30321d7..eacc31d 100644 --- a/core/src/services/session_service.rs +++ b/core/src/services/session_service.rs @@ -77,7 +77,6 @@ mod tests { ) .await .unwrap(); - // println!("{}", session.err().unwrap().1); assert!( ApplicationService::auth(db, Uuid::parse_str(&session).unwrap()) .await diff --git a/core/src/utils/csv.rs b/core/src/utils/csv.rs index ce6827f..61b7e1c 100644 --- a/core/src/utils/csv.rs +++ b/core/src/utils/csv.rs @@ -9,6 +9,7 @@ use sea_orm::DbConn; impl From<(i32, ApplicationDetails)> for ApplicationRow { fn from((application, d): (i32, ApplicationDetails)) -> Self { let c = d.candidate; + let (diploma_1_8, diploma_2_8, diploma_1_9) = c.grades.group_by_semester(); Self { application, name: Some(c.name), @@ -20,9 +21,18 @@ impl From<(i32, ApplicationDetails)> for ApplicationRow { citizenship: Some(c.citizenship), email: Some(c.email), sex: Some(c.sex), + personal_identification_number: Some(c.personal_id_number), health_insurance: Some(c.health_insurance), school_name: Some(c.school_name), - personal_identification_number: Some(c.personal_id_number), + + diploma_1_8: diploma_1_8.to_string(), + diploma_2_8: diploma_2_8.to_string(), + diploma_1_9: diploma_1_9.to_string(), + + first_school_name: Some(c.first_school.name().to_owned()), + first_school_field: Some(c.first_school.field().to_owned()), + second_school_name: Some(c.second_school.name().to_owned()), + second_school_field: Some(c.second_school.field().to_owned()), parent_name: d.parents.get(0).map(|p| p.name.clone()), parent_surname: d.parents.get(0).map(|p| p.surname.clone()), From 79d5a299595d771bafb99fec00b7d84ace6326b1 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Thu, 19 Jan 2023 20:18:40 +0100 Subject: [PATCH 4/9] feat!: letterAddress, birthSurname fields --- api/src/routes/candidate.rs | 2 + core/src/database/mutation/candidate.rs | 2 + core/src/models/candidate.rs | 2 + core/src/models/candidate_details.rs | 101 +++++++++++------- core/src/services/parent_service.rs | 2 + entity/src/candidate.rs | 1 + .../src/m20221024_121621_create_candidate.rs | 2 + 7 files changed, 71 insertions(+), 41 deletions(-) diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index 0fab9db..7c7775b 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -308,9 +308,11 @@ mod tests { \"candidate\": { \"name\": \"idk\", \"surname\": \"idk\", + \"birthSurname\": \"surname\", \"birthplace\": \"Praha 1\", \"birthdate\": \"2015-09-18\", \"address\": \"Stefanikova jidelna\", + \"letterAddress\": \"Stefanikova jidelna\", \"telephone\": \"000111222333\", \"citizenship\": \"Czech Republic\", \"email\": \"magor@magor.cz\", diff --git a/core/src/database/mutation/candidate.rs b/core/src/database/mutation/candidate.rs index aef582b..e5bbdc0 100644 --- a/core/src/database/mutation/candidate.rs +++ b/core/src/database/mutation/candidate.rs @@ -44,9 +44,11 @@ impl Mutation { candidate.name = Set(enc_candidate.name.map(|e| e.into())); candidate.surname = Set(enc_candidate.surname.map(|e| e.into())); + candidate.birth_surname = Set(enc_candidate.birth_surname.map(|e| e.into())); candidate.birthplace = Set(enc_candidate.birthplace.map(|e| e.into())); candidate.birthdate = Set(enc_candidate.birthdate.map(|e| e.into())); candidate.address = Set(enc_candidate.address.map(|e| e.into())); + candidate.letter_address = Set(enc_candidate.letter_address.map(|e| e.into())); candidate.telephone = Set(enc_candidate.telephone.map(|e| e.into())); candidate.citizenship = Set(enc_candidate.citizenship.map(|e| e.into())); candidate.email = Set(enc_candidate.email.map(|e| e.into())); diff --git a/core/src/models/candidate.rs b/core/src/models/candidate.rs index 920e387..5eb16f8 100644 --- a/core/src/models/candidate.rs +++ b/core/src/models/candidate.rs @@ -62,9 +62,11 @@ pub struct CreateCandidateResponse { pub struct CandidateDetails { pub name: String, pub surname: String, + pub birth_surname: String, pub birthplace: String, pub birthdate: NaiveDate, pub address: String, + pub letter_address: String, pub telephone: String, pub citizenship: String, pub email: String, diff --git a/core/src/models/candidate_details.rs b/core/src/models/candidate_details.rs index 56b4ee7..335b83c 100644 --- a/core/src/models/candidate_details.rs +++ b/core/src/models/candidate_details.rs @@ -16,9 +16,11 @@ pub struct EncryptedString(String); pub struct EncryptedCandidateDetails { pub name: Option, pub surname: Option, + pub birth_surname: Option, pub birthplace: Option, pub birthdate: Option, pub address: Option, + pub letter_address: Option, pub telephone: Option, pub citizenship: Option, pub email: Option, @@ -128,9 +130,11 @@ impl EncryptedCandidateDetails { let d = tokio::try_join!( EncryptedString::new_option(&form.name, recipients), EncryptedString::new_option(&form.surname, recipients), + EncryptedString::new_option(&form.birth_surname, recipients), EncryptedString::new_option(&form.birthplace, recipients), EncryptedString::new_option(&birthdate_str, recipients), EncryptedString::new_option(&form.address, recipients), + EncryptedString::new_option(&form.letter_address, recipients), EncryptedString::new_option(&form.telephone, recipients), EncryptedString::new_option(&form.citizenship, recipients), EncryptedString::new_option(&form.email, recipients), @@ -147,19 +151,21 @@ impl EncryptedCandidateDetails { EncryptedCandidateDetails { name: d.0, surname: d.1, - birthplace: d.2, - birthdate: d.3, - address: d.4, - telephone: d.5, - citizenship: d.6, - email: d.7, - sex: d.8, - personal_id_number: d.9, - school_name: d.10, - health_insurance: d.11, - grades_json: d.12, - first_school: d.13, - second_school: d.14, + birth_surname: d.2, + birthplace: d.3, + birthdate: d.4, + address: d.5, + letter_address: d.6, + telephone: d.7, + citizenship: d.8, + email: d.9, + sex: d.10, + personal_id_number: d.11, + school_name: d.12, + health_insurance: d.13, + grades_json: d.14, + first_school: d.15, + second_school: d.16, test_language: Some(form.test_language.to_owned()), } ) @@ -169,37 +175,41 @@ impl EncryptedCandidateDetails { let d = tokio::try_join!( EncryptedString::decrypt_option(&self.name, priv_key), // 0 EncryptedString::decrypt_option(&self.surname, priv_key), // 1 - EncryptedString::decrypt_option(&self.birthplace, priv_key), // 2 - EncryptedString::decrypt_option(&self.birthdate, priv_key), // 3 - EncryptedString::decrypt_option(&self.address, priv_key), // 4 - EncryptedString::decrypt_option(&self.telephone, priv_key), // 5 - EncryptedString::decrypt_option(&self.citizenship, priv_key), // 6 - EncryptedString::decrypt_option(&self.email, priv_key), // 7 - EncryptedString::decrypt_option(&self.sex, priv_key), // 8 - EncryptedString::decrypt_option(&self.personal_id_number, priv_key),// 9 - EncryptedString::decrypt_option(&self.school_name, priv_key), // 10 - EncryptedString::decrypt_option(&self.health_insurance, priv_key), // 11 - EncryptedString::decrypt_option(&self.grades_json, priv_key), // 12 - EncryptedString::decrypt_option(&self.first_school, priv_key), // 13 - EncryptedString::decrypt_option(&self.second_school, priv_key), // 14 + EncryptedString::decrypt_option(&self.birth_surname, priv_key), // 2 + EncryptedString::decrypt_option(&self.birthplace, priv_key), // 3 + EncryptedString::decrypt_option(&self.birthdate, priv_key), // 4 + EncryptedString::decrypt_option(&self.address, priv_key), // 5 + EncryptedString::decrypt_option(&self.letter_address, priv_key), // 6 + EncryptedString::decrypt_option(&self.telephone, priv_key), // 7 + EncryptedString::decrypt_option(&self.citizenship, priv_key), // 8 + EncryptedString::decrypt_option(&self.email, priv_key), // 9 + EncryptedString::decrypt_option(&self.sex, priv_key), // 10 + EncryptedString::decrypt_option(&self.personal_id_number, priv_key),// 11 + EncryptedString::decrypt_option(&self.school_name, priv_key), // 12 + EncryptedString::decrypt_option(&self.health_insurance, priv_key), // 13 + EncryptedString::decrypt_option(&self.grades_json, priv_key), // 14 + EncryptedString::decrypt_option(&self.first_school, priv_key), // 15 + EncryptedString::decrypt_option(&self.second_school, priv_key), // 16 )?; Ok(CandidateDetails { name: d.0.unwrap_or_default(), surname: d.1.unwrap_or_default(), - birthplace: d.2.unwrap_or_default(), - birthdate: parse_naive_date_from_opt_str(d.3, NAIVE_DATE_FMT)?, - address: d.4.unwrap_or_default(), - telephone: d.5.unwrap_or_default(), - citizenship: d.6.unwrap_or_default(), - email: d.7.unwrap_or_default(), - sex: d.8.unwrap_or_default(), - personal_id_number: d.9.unwrap_or_default(), - school_name: d.10.unwrap_or_default(), - health_insurance: d.11.unwrap_or_default(), - grades: GradeList::from_opt_str(d.12).unwrap_or_default(), - first_school: School::from_opt_str(d.13).unwrap_or_default(), - second_school: School::from_opt_str(d.14).unwrap_or_default(), + birth_surname: d.2.unwrap_or_default(), + birthplace: d.3.unwrap_or_default(), + birthdate: parse_naive_date_from_opt_str(d.4, NAIVE_DATE_FMT)?, + address: d.5.unwrap_or_default(), + letter_address: d.6.unwrap_or_default(), + telephone: d.7.unwrap_or_default(), + citizenship: d.8.unwrap_or_default(), + email: d.9.unwrap_or_default(), + sex: d.10.unwrap_or_default(), + personal_id_number: d.11.unwrap_or_default(), + school_name: d.12.unwrap_or_default(), + health_insurance: d.13.unwrap_or_default(), + grades: GradeList::from_opt_str(d.14).unwrap_or_default(), + first_school: School::from_opt_str(d.15).unwrap_or_default(), + second_school: School::from_opt_str(d.16).unwrap_or_default(), test_language: self.test_language.to_owned().unwrap_or_default().to_string(), } ) @@ -214,8 +224,13 @@ impl EncryptedCandidateDetails { self.telephone.is_some() && self.citizenship.is_some() && self.email.is_some() && - // self.sex.is_some() && - self.personal_id_number.is_some() + self.personal_id_number.is_some() && + self.school_name.is_some() && + self.health_insurance.is_some() && + self.grades_json.is_some() && + self.first_school.is_some() && + self.second_school.is_some() + } } impl From<&candidate::Model> for EncryptedCandidateDetails { @@ -225,9 +240,11 @@ impl From<&candidate::Model> for EncryptedCandidateDetails { EncryptedCandidateDetails { name: EncryptedString::try_from(&candidate.name).ok(), surname: EncryptedString::try_from(&candidate.surname).ok(), + birth_surname: EncryptedString::try_from(&candidate.birth_surname).ok(), birthplace: EncryptedString::try_from(&candidate.birthplace).ok(), birthdate: EncryptedString::try_from(&candidate.birthdate).ok(), address: EncryptedString::try_from(&candidate.address).ok(), + letter_address: EncryptedString::try_from(&candidate.letter_address).ok(), telephone: EncryptedString::try_from(&candidate.telephone).ok(), citizenship: EncryptedString::try_from(&candidate.citizenship).ok(), email: EncryptedString::try_from(&candidate.email).ok(), @@ -377,9 +394,11 @@ pub mod tests { candidate: CandidateDetails { name: "name".to_string(), surname: "surname".to_string(), + birth_surname: "birth_surname".to_string(), birthplace: "birthplace".to_string(), birthdate: chrono::NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(), address: "address".to_string(), + letter_address: "letter_address".to_string(), telephone: "telephone".to_string(), citizenship: "citizenship".to_string(), email: "email".to_string(), diff --git a/core/src/services/parent_service.rs b/core/src/services/parent_service.rs index a24636f..74cb8d0 100644 --- a/core/src/services/parent_service.rs +++ b/core/src/services/parent_service.rs @@ -61,9 +61,11 @@ mod tests { candidate: CandidateDetails { name: "name".to_string(), surname: "surname".to_string(), + birth_surname: "birth_surname".to_string(), birthplace: "birthplace".to_string(), birthdate: chrono::NaiveDate::from_ymd_opt(2000, 1, 1).unwrap(), address: "address".to_string(), + letter_address: "letter_address".to_string(), telephone: "telephone".to_string(), citizenship: "citizenship".to_string(), email: "email".to_string(), diff --git a/entity/src/candidate.rs b/entity/src/candidate.rs index a48d4db..804f66e 100644 --- a/entity/src/candidate.rs +++ b/entity/src/candidate.rs @@ -13,6 +13,7 @@ pub struct Model { pub birthplace: Option, pub birthdate: Option, pub address: Option, + pub letter_address: Option, pub telephone: Option, pub citizenship: Option, pub email: Option, diff --git a/migration/src/m20221024_121621_create_candidate.rs b/migration/src/m20221024_121621_create_candidate.rs index 4e46469..1b43bdc 100644 --- a/migration/src/m20221024_121621_create_candidate.rs +++ b/migration/src/m20221024_121621_create_candidate.rs @@ -24,6 +24,7 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(Candidate::Birthplace).string()) .col(ColumnDef::new(Candidate::Birthdate).string()) .col(ColumnDef::new(Candidate::Address).string()) + .col(ColumnDef::new(Candidate::LetterAddress).string()) .col(ColumnDef::new(Candidate::Telephone).string()) .col(ColumnDef::new(Candidate::Citizenship).string()) .col(ColumnDef::new(Candidate::Email).string()) @@ -60,6 +61,7 @@ pub enum Candidate { Birthplace, Birthdate, Address, + LetterAddress, Telephone, Citizenship, Email, From 339921a131291334efde4be62d34743ad4c772cb Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Thu, 19 Jan 2023 22:37:35 +0100 Subject: [PATCH 5/9] feat: birth surname & letter address in csv --- core/src/models/application.rs | 2 ++ core/src/utils/csv.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/core/src/models/application.rs b/core/src/models/application.rs index 6224cf0..6ffe645 100644 --- a/core/src/models/application.rs +++ b/core/src/models/application.rs @@ -47,9 +47,11 @@ pub struct ApplicationRow { pub application: i32, pub name: Option, pub surname: Option, + pub birth_surname: Option, pub birthplace: Option, pub birthdate: Option, pub address: Option, + pub letter_address: Option, pub telephone: Option, pub citizenship: Option, pub email: Option, diff --git a/core/src/utils/csv.rs b/core/src/utils/csv.rs index 61b7e1c..4a09f61 100644 --- a/core/src/utils/csv.rs +++ b/core/src/utils/csv.rs @@ -14,9 +14,11 @@ impl From<(i32, ApplicationDetails)> for ApplicationRow { application, name: Some(c.name), surname: Some(c.surname), + birth_surname: Some(c.birth_surname), birthplace: Some(c.birthplace), birthdate: Some(c.birthdate.to_string()), address: Some(c.address), + letter_address: Some(c.letter_address), telephone: Some(c.telephone), citizenship: Some(c.citizenship), email: Some(c.email), From 9c2d1e18a2d763fd2c3551a2c83a5f70f2b08607 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Thu, 19 Jan 2023 22:48:05 +0100 Subject: [PATCH 6/9] refactor: EncDetails from &parent::Model instead of parent::Model --- core/src/error.rs | 3 +++ core/src/models/candidate_details.rs | 6 +++--- core/src/services/application_service.rs | 4 ++-- core/src/services/candidate_service.rs | 2 +- core/src/services/parent_service.rs | 2 +- core/src/utils/csv.rs | 2 +- 6 files changed, 11 insertions(+), 8 deletions(-) diff --git a/core/src/error.rs b/core/src/error.rs index 5273c4c..ee3ac40 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -14,6 +14,8 @@ pub enum ServiceError { Forbidden, #[error("Session expired, please login again")] ExpiredSession, + #[error("Missing details")] + MissingDetails, #[error("User already exists")] UserAlreadyExists, #[error("Candidate not found")] @@ -82,6 +84,7 @@ impl ServiceError { // 4XX ServiceError::InvalidApplicationId => 400, ServiceError::ParentOverflow => 400, + ServiceError::MissingDetails => 400, ServiceError::Unauthorized => 401, ServiceError::InvalidCredentials => 401, ServiceError::ExpiredSession => 401, diff --git a/core/src/models/candidate_details.rs b/core/src/models/candidate_details.rs index 335b83c..e3f95b0 100644 --- a/core/src/models/candidate_details.rs +++ b/core/src/models/candidate_details.rs @@ -358,9 +358,9 @@ impl EncryptedApplicationDetails { } } -impl From<(&candidate::Model, Vec)> for EncryptedApplicationDetails { +impl From<(&candidate::Model, &Vec)> for EncryptedApplicationDetails { fn from( - (candidate, parents): (&candidate::Model, Vec), + (candidate, parents): (&candidate::Model, &Vec), ) -> Self { let enc_parents = parents.iter() .map(|m| EncryptedParentDetails::from(m)) @@ -509,7 +509,7 @@ pub mod tests { let (_, candidate, parents) = put_user_data(&db).await; - let encrypted_details = EncryptedApplicationDetails::try_from((&candidate, parents)).unwrap(); + let encrypted_details = EncryptedApplicationDetails::try_from((&candidate, &parents)).unwrap(); let application_details = encrypted_details .decrypt(PRIVATE_KEY.to_string()) // decrypt with admin's private key diff --git a/core/src/services/application_service.rs b/core/src/services/application_service.rs index 693b0f6..991184c 100644 --- a/core/src/services/application_service.rs +++ b/core/src/services/application_service.rs @@ -237,7 +237,7 @@ impl ApplicationService { let candidate = ApplicationService::find_related_candidate(db, application).await?; let parents = Query::find_candidate_parents(db, &candidate).await?; - let enc_details = EncryptedApplicationDetails::from((&candidate, parents)); + let enc_details = EncryptedApplicationDetails::from((&candidate, &parents)); if enc_details.is_filled() { enc_details.decrypt(private_key).await @@ -330,7 +330,7 @@ impl ApplicationService { recipients.append(&mut admin_public_keys); recipients.append(&mut applications.iter().map(|a| a.public_key.to_owned()).collect()); - let dec_details = EncryptedApplicationDetails::from((&candidate, parents.clone())) + let dec_details = EncryptedApplicationDetails::from((&candidate, &parents)) .decrypt(admin_private_key).await?; let enc_details = EncryptedApplicationDetails::new(&dec_details, recipients).await?; diff --git a/core/src/services/candidate_service.rs b/core/src/services/candidate_service.rs index 46a4976..223b8c1 100644 --- a/core/src/services/candidate_service.rs +++ b/core/src/services/candidate_service.rs @@ -134,7 +134,7 @@ pub mod tests { let dec_priv_key = crypto::decrypt_password(application.private_key.clone(), password) .await .unwrap(); - let enc_details = EncryptedApplicationDetails::try_from((&enc_candidate, enc_parent)) + let enc_details = EncryptedApplicationDetails::try_from((&enc_candidate, &enc_parent)) .ok() .unwrap(); let dec_details = enc_details.decrypt(dec_priv_key).await.ok().unwrap(); diff --git a/core/src/services/parent_service.rs b/core/src/services/parent_service.rs index 74cb8d0..2367b0f 100644 --- a/core/src/services/parent_service.rs +++ b/core/src/services/parent_service.rs @@ -117,7 +117,7 @@ mod tests { .unwrap(); let priv_key = crypto::decrypt_password(application.private_key.clone(), plain_text_password).await.unwrap(); - let dec_details = EncryptedApplicationDetails::try_from((&candidate, parents)) + let dec_details = EncryptedApplicationDetails::try_from((&candidate, &parents)) .unwrap() .decrypt(priv_key) .await diff --git a/core/src/utils/csv.rs b/core/src/utils/csv.rs index 4a09f61..ff4d740 100644 --- a/core/src/utils/csv.rs +++ b/core/src/utils/csv.rs @@ -57,7 +57,7 @@ pub async fn export(db: &DbConn, private_key: String) -> Result, Service let candidate = ApplicationService::find_related_candidate(db, &application).await?; let parents = Query::find_candidate_parents(db, &candidate).await?; - let row: ApplicationRow = match EncryptedApplicationDetails::try_from((&candidate, parents)) + let row: ApplicationRow = match EncryptedApplicationDetails::try_from((&candidate, &parents)) { Ok(d) => ApplicationRow::from( d.decrypt(private_key.to_string()) From 5392b6fd0d8d09cd4a97d919ae53da052fd66d27 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 20 Jan 2023 13:06:50 +0100 Subject: [PATCH 7/9] refactor: remove duplicate char reference --- core/src/crypto.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/core/src/crypto.rs b/core/src/crypto.rs index 0d2ded8..05e2644 100644 --- a/core/src/crypto.rs +++ b/core/src/crypto.rs @@ -34,10 +34,10 @@ pub fn random_12_char_string() -> String { /// Exclude O and 0, lowercase letters fn is_usable_char(c: &char) -> bool { - ('1'..='9').contains(&c) || - ('A'..='N').contains(&c) || - ('P'..'Z').contains(&c) || - ['@', '#', '$', '%'].contains(&c) + ('1'..='9').contains(c) || + ('A'..='N').contains(c) || + ('P'..'Z').contains(c) || + ['@', '#', '$', '%'].contains(c) } pub async fn hash_password(password_plain_text: String) -> Result { From ddb07a342d03d114e3c19c9cb67795a28e60ca93 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 20 Jan 2023 13:07:50 +0100 Subject: [PATCH 8/9] chore: validator dependency --- Cargo.lock | 79 ++++++++++++++++++++++++++++++++++++++++++++++++- core/Cargo.toml | 2 ++ 2 files changed, 80 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 565c0bc..67ee5f8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -122,6 +122,15 @@ dependencies = [ "version_check", ] +[[package]] +name = "aho-corasick" +version = "0.7.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc936419f96fa211c1b9166887b38e5e40b19958e5b895be7c1f93adec7071ac" +dependencies = [ + "memchr", +] + [[package]] name = "aliasable" version = "0.1.3" @@ -1489,6 +1498,17 @@ dependencies = [ "cxx-build", ] +[[package]] +name = "idna" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] + [[package]] name = "idna" version = "0.3.0" @@ -1499,6 +1519,12 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "if_chain" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb56e1aa765b4b4f3aadfab769793b7087bb03a4ea4920644a6d238e2df5b9ed" + [[package]] name = "indexmap" version = "1.9.2" @@ -1701,6 +1727,12 @@ dependencies = [ "regex-automata", ] +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + [[package]] name = "md-5" version = "0.10.5" @@ -2170,6 +2202,7 @@ dependencies = [ "serial_test", "thiserror", "tokio", + "validator", ] [[package]] @@ -2399,6 +2432,8 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48aaa5748ba571fb95cd2c85c09f629215d3a6ece942baa100950af03a34f733" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] @@ -3595,7 +3630,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d68c799ae75762b8c3fe375feb6600ef5602c883c5d21eb51c09f22b83c4643" dependencies = [ "form_urlencoded", - "idna", + "idna 0.3.0", "percent-encoding", ] @@ -3609,6 +3644,48 @@ dependencies = [ "serde", ] +[[package]] +name = "validator" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f07b0a1390e01c0fc35ebb26b28ced33c9a3808f7f9fbe94d3cc01e233bfeed5" +dependencies = [ + "idna 0.2.3", + "lazy_static", + "regex", + "serde", + "serde_derive", + "serde_json", + "url", + "validator_derive", +] + +[[package]] +name = "validator_derive" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea7ed5e8cf2b6bdd64a6c4ce851da25388a89327b17b88424ceced6bd5017923" +dependencies = [ + "if_chain", + "lazy_static", + "proc-macro-error", + "proc-macro2", + "quote", + "regex", + "syn", + "validator_types", +] + +[[package]] +name = "validator_types" +version = "0.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ddf34293296847abfc1493b15c6e2f5d3cd19f57ad7d22673bf4c6278da329" +dependencies = [ + "proc-macro2", + "syn", +] + [[package]] name = "valuable" version = "0.1.0" diff --git a/core/Cargo.toml b/core/Cargo.toml index b7525d2..b90efc3 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -13,6 +13,8 @@ portfolio-entity = { path = "../entity" } serde = { version = "^1.0", features = ["derive"] } serde_json = "1.0" +validator = { version = "^0.15", features = ["derive"] } + # csv csv = "^1.1" From 7bf6d7e938716c83af368b8bac9cea83eeba42a1 Mon Sep 17 00:00:00 2001 From: Sebastian Pravda Date: Fri, 20 Jan 2023 13:07:58 +0100 Subject: [PATCH 9/9] feat: candidate details validation --- api/src/routes/candidate.rs | 1 + core/src/error.rs | 3 +++ core/src/models/candidate.rs | 22 +++++++++++++++++++++- core/src/models/candidate_details.rs | 1 - core/src/models/school.rs | 12 +++++++++++- 5 files changed, 36 insertions(+), 3 deletions(-) diff --git a/api/src/routes/candidate.rs b/api/src/routes/candidate.rs index 7c7775b..0dcb1ce 100644 --- a/api/src/routes/candidate.rs +++ b/api/src/routes/candidate.rs @@ -102,6 +102,7 @@ pub async fn post_details( ) -> Result, Custom> { let db = conn.into_inner(); let form = details.into_inner(); + form.candidate.validate_self().map_err(to_custom_error)?; let application: application::Model = session.into(); let candidate = ApplicationService::find_related_candidate(&db, &application).await.map_err(to_custom_error)?; // TODO diff --git a/core/src/error.rs b/core/src/error.rs index ee3ac40..22685ec 100644 --- a/core/src/error.rs +++ b/core/src/error.rs @@ -16,6 +16,8 @@ pub enum ServiceError { ExpiredSession, #[error("Missing details")] MissingDetails, + #[error("Validation error: {0}")] + ValidationError(#[from] validator::ValidationErrors), #[error("User already exists")] UserAlreadyExists, #[error("Candidate not found")] @@ -85,6 +87,7 @@ impl ServiceError { ServiceError::InvalidApplicationId => 400, ServiceError::ParentOverflow => 400, ServiceError::MissingDetails => 400, + ServiceError::ValidationError(_) => 400, ServiceError::Unauthorized => 401, ServiceError::InvalidCredentials => 401, ServiceError::ExpiredSession => 401, diff --git a/core/src/models/candidate.rs b/core/src/models/candidate.rs index 5eb16f8..521cade 100644 --- a/core/src/models/candidate.rs +++ b/core/src/models/candidate.rs @@ -1,6 +1,7 @@ use chrono::NaiveDate; use entity::{application, candidate}; use serde::{Deserialize, Serialize}; +use validator::Validate; use crate::{ error::ServiceError, @@ -57,28 +58,47 @@ pub struct CreateCandidateResponse { pub password: String, } -#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +#[derive(Debug, Serialize, Deserialize, Validate, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct CandidateDetails { + #[validate(length(min = 1, max = 255))] pub name: String, + #[validate(length(min = 1, max = 255))] pub surname: String, + #[validate(length(min = 1, max = 255))] pub birth_surname: String, + #[validate(length(min = 1, max = 255))] pub birthplace: String, pub birthdate: NaiveDate, + #[validate(length(min = 1, max = 255))] pub address: String, pub letter_address: String, + #[validate(length(min = 1, max = 31))] pub telephone: String, + #[validate(length(min = 1, max = 255))] pub citizenship: String, + #[validate(email)] pub email: String, pub sex: String, + #[validate(length(min = 1, max = 255))] pub personal_id_number: String, + #[validate(length(min = 1, max = 255))] pub school_name: String, + #[validate(length(min = 1, max = 255))] pub health_insurance: String, pub grades: GradeList, pub first_school: School, pub second_school: School, pub test_language: String, } +impl CandidateDetails { + pub fn validate_self(&self) -> Result<(), ServiceError> { + self.first_school.validate()?; + self.second_school.validate()?; + self.validate() + .map_err(ServiceError::ValidationError) + } +} #[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] #[serde(rename_all = "camelCase")] pub struct ParentDetails { diff --git a/core/src/models/candidate_details.rs b/core/src/models/candidate_details.rs index e3f95b0..836e903 100644 --- a/core/src/models/candidate_details.rs +++ b/core/src/models/candidate_details.rs @@ -227,7 +227,6 @@ impl EncryptedCandidateDetails { self.personal_id_number.is_some() && self.school_name.is_some() && self.health_insurance.is_some() && - self.grades_json.is_some() && self.first_school.is_some() && self.second_school.is_some() diff --git a/core/src/models/school.rs b/core/src/models/school.rs index 27c18d6..a04b84d 100644 --- a/core/src/models/school.rs +++ b/core/src/models/school.rs @@ -1,8 +1,13 @@ use serde::{Serialize, Deserialize}; +use validator::Validate; -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)] +use crate::error::ServiceError; + +#[derive(Debug, Clone, Serialize, Deserialize, Validate, PartialEq, Eq)] pub struct School { + #[validate(length(min = 1, max = 255))] name: String, + #[validate(length(min = 1, max = 255))] field: String, } @@ -13,6 +18,11 @@ impl School { ) } + pub fn validate_self(&self) -> Result<(), ServiceError> { + self.validate() + .map_err(ServiceError::ValidationError) + } + pub fn name(&self) -> &str { &self.name }