mirror of
https://github.com/danbulant/Portfolio
synced 2026-06-19 14:31:05 +00:00
feat: two parents
This commit is contained in:
parent
fa9af8f8ee
commit
99d964cae4
10 changed files with 113 additions and 65 deletions
|
|
@ -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}, models::candidate::{BaseCandidateResponse, CreateCandidateResponse, 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, Query, error::ServiceError,
|
||||
};
|
||||
use requests::{AdminLoginRequest, RegisterRequest};
|
||||
use rocket::http::{Cookie, Status, CookieJar};
|
||||
|
|
@ -146,10 +146,15 @@ pub async fn get_candidate(
|
|||
let db = conn.into_inner();
|
||||
let private_key = session.get_private_key();
|
||||
|
||||
let candidate = Query::find_candidate_by_id(db, id)
|
||||
.await
|
||||
.map_err(|e| to_custom_error(ServiceError::Forbidden))? // TODO better error handling
|
||||
.ok_or(to_custom_error(ServiceError::CandidateNotFound))?;
|
||||
|
||||
let details = ApplicationService::decrypt_all_details(
|
||||
private_key,
|
||||
db,
|
||||
id
|
||||
candidate
|
||||
)
|
||||
.await
|
||||
.map_err(to_custom_error)?;
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ pub async fn post_details(
|
|||
let form = details.into_inner();
|
||||
let candidate: entity::candidate::Model = session.into();
|
||||
|
||||
let _candidate_parent = ApplicationService::add_all_details(db, candidate.application, &form)
|
||||
let _candidate_parent = ApplicationService::add_all_details(db, candidate, &form)
|
||||
.await
|
||||
.map_err(to_custom_error)?;
|
||||
|
||||
|
|
@ -97,7 +97,7 @@ pub async fn get_details(
|
|||
let candidate: entity::candidate::Model = session.into();
|
||||
|
||||
// let handle = tokio::spawn(async move {
|
||||
let details = ApplicationService::decrypt_all_details(private_key, db, candidate.application)
|
||||
let details = ApplicationService::decrypt_all_details(private_key, db, candidate)
|
||||
.await
|
||||
.map(|x| Json(x))
|
||||
.map_err(to_custom_error);
|
||||
|
|
|
|||
|
|
@ -20,15 +20,15 @@ impl Mutation {
|
|||
parent: Model,
|
||||
enc_parent: EncryptedParentDetails,
|
||||
) -> Result<Model, sea_orm::DbErr> {
|
||||
let mut user: parent::ActiveModel = parent.into();
|
||||
user.name = Set(Some(enc_parent.name.into()));
|
||||
user.surname = Set(Some(enc_parent.surname.into()));
|
||||
user.telephone = Set(Some(enc_parent.telephone.into()));
|
||||
user.email = Set(Some(enc_parent.email.into()));
|
||||
let mut parent: parent::ActiveModel = parent.into();
|
||||
parent.name = Set(Some(enc_parent.name.into()));
|
||||
parent.surname = Set(Some(enc_parent.surname.into()));
|
||||
parent.telephone = Set(Some(enc_parent.telephone.into()));
|
||||
parent.email = Set(Some(enc_parent.email.into()));
|
||||
|
||||
user.updated_at = Set(chrono::offset::Local::now().naive_local());
|
||||
parent.updated_at = Set(chrono::offset::Local::now().naive_local());
|
||||
|
||||
user.update(db).await
|
||||
parent.update(db).await
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -56,9 +56,9 @@ mod tests {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
Mutation::create_parent(&db, APPLICATION_ID).await.unwrap();
|
||||
let new_parent = Mutation::create_parent(&db, APPLICATION_ID).await.unwrap();
|
||||
|
||||
let parent = Query::find_parent_by_id(&db, APPLICATION_ID).await.unwrap();
|
||||
let parent = Query::find_parent_by_id(&db, new_parent.id).await.unwrap();
|
||||
assert!(parent.is_some());
|
||||
}
|
||||
|
||||
|
|
@ -88,11 +88,11 @@ mod tests {
|
|||
.await
|
||||
.unwrap();
|
||||
|
||||
Mutation::add_parent_details(&db, parent, encrypted_details.parents[0].clone())
|
||||
Mutation::add_parent_details(&db, parent.clone(), encrypted_details.parents[0].clone())
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let parent = Query::find_parent_by_id(&db, APPLICATION_ID)
|
||||
let parent = Query::find_parent_by_id(&db, parent.id)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
|
|
|||
|
|
@ -13,10 +13,10 @@ impl Query {
|
|||
#[deprecated(note = "Use find_candidate_parents instead")]
|
||||
pub async fn find_parent_by_id(
|
||||
db: &DbConn,
|
||||
application_id: i32,
|
||||
id: i32,
|
||||
) -> Result<Option<Model>, DbErr> {
|
||||
|
||||
Entity::find_by_id(application_id).one(db).await
|
||||
Entity::find_by_id(id).one(db).await
|
||||
}
|
||||
|
||||
// TODO limit to two parents??
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ pub enum ServiceError {
|
|||
#[error("Parrent not found")]
|
||||
ParentNotFound,
|
||||
#[error("Database error")]
|
||||
ParentOverflow,
|
||||
#[error("Too many parents")]
|
||||
DbError(#[from] sea_orm::DbErr),
|
||||
#[error("User not found, please contact technical support")]
|
||||
UserNotFoundByJwtId,
|
||||
|
|
@ -78,6 +80,7 @@ impl ServiceError {
|
|||
ServiceError::UserAlreadyExists => 409,
|
||||
ServiceError::CandidateNotFound => 404,
|
||||
ServiceError::ParentNotFound => 500,
|
||||
ServiceError::ParentOverflow => 400, // TODO: correct error code
|
||||
ServiceError::DbError(_) => 500,
|
||||
ServiceError::UserNotFoundByJwtId => 500,
|
||||
ServiceError::UserNotFoundBySessionId => 500,
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ use super::candidate::{CandidateDetails, ParentDetails};
|
|||
|
||||
pub const NAIVE_DATE_FMT: &str = "%Y-%m-%d";
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncryptedString(String);
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncryptedCandidateDetails {
|
||||
pub name: EncryptedString,
|
||||
pub surname: EncryptedString,
|
||||
|
|
@ -26,14 +26,14 @@ pub struct EncryptedCandidateDetails {
|
|||
pub study: String,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncryptedParentDetails {
|
||||
pub name: EncryptedString,
|
||||
pub surname: EncryptedString,
|
||||
pub telephone: EncryptedString,
|
||||
pub email: EncryptedString,
|
||||
}
|
||||
#[derive(Clone)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EncryptedApplicationDetails {
|
||||
pub candidate: EncryptedCandidateDetails,
|
||||
pub parents: Vec<EncryptedParentDetails>,
|
||||
|
|
@ -274,15 +274,21 @@ impl EncryptedApplicationDetails {
|
|||
}
|
||||
|
||||
// TODO: use different metehod for this
|
||||
impl TryFrom<(candidate::Model, parent::Model)> for EncryptedApplicationDetails {
|
||||
impl TryFrom<(candidate::Model, Vec<parent::Model>)> for EncryptedApplicationDetails {
|
||||
type Error = ServiceError;
|
||||
|
||||
fn try_from(
|
||||
(candidate, parent): (candidate::Model, parent::Model),
|
||||
(candidate, parents): (candidate::Model, Vec<parent::Model>),
|
||||
) -> Result<Self, Self::Error> {
|
||||
let mut enc_parents = vec![];
|
||||
for parent in parents.iter() {
|
||||
enc_parents.push(
|
||||
EncryptedParentDetails::try_from(parent.clone())?
|
||||
);
|
||||
}
|
||||
Ok(EncryptedApplicationDetails {
|
||||
candidate: EncryptedCandidateDetails::try_from(candidate)?,
|
||||
parents: vec![EncryptedParentDetails::try_from(parent)?],
|
||||
parents: enc_parents,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -358,10 +364,10 @@ pub mod tests {
|
|||
study: "study".to_string(),
|
||||
},
|
||||
parents: vec![ParentDetails {
|
||||
email: "parent_email".to_string(),
|
||||
name: "parent_name".to_string(),
|
||||
surname: "parent_surname".to_string(),
|
||||
telephone: "parent_telephone".to_string()
|
||||
telephone: "parent_telephone".to_string(),
|
||||
email: "parent_email".to_string(),
|
||||
}]
|
||||
})
|
||||
);
|
||||
|
|
|
|||
|
|
@ -24,37 +24,26 @@ impl ApplicationService {
|
|||
|
||||
pub async fn add_all_details(
|
||||
db: &DbConn,
|
||||
application: i32,
|
||||
candidate: candidate::Model,
|
||||
form: &ApplicationDetails,
|
||||
) -> Result<(candidate::Model, parent::Model), ServiceError> {
|
||||
let candidate = Query::find_candidate_by_id(db, application)
|
||||
.await?
|
||||
.ok_or(ServiceError::CandidateNotFound)?;
|
||||
|
||||
let parent = Query::find_candidate_parents(db, candidate.clone())
|
||||
.await?;
|
||||
|
||||
let recipients = get_recipients(db, &candidate.public_key).await?;
|
||||
|
||||
let enc_details = EncryptedApplicationDetails::new(form, recipients).await?;
|
||||
) -> Result<(candidate::Model, Vec<parent::Model>), ServiceError> { // TODO: is this service needed?
|
||||
|
||||
Ok(
|
||||
tokio::try_join!(
|
||||
CandidateService::add_candidate_details(db, candidate, enc_details.candidate),
|
||||
ParentService::add_parent_details(db, parent[0].clone(), enc_details.parents[0].clone()) // TODO
|
||||
)?
|
||||
(
|
||||
CandidateService::add_candidate_details(db, candidate.clone(), &form.candidate).await?,
|
||||
ParentService::add_parents_details(db, candidate, &form.parents).await?
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn decrypt_all_details(
|
||||
private_key: String,
|
||||
db: &DbConn,
|
||||
application_id: i32,
|
||||
candidate: candidate::Model,
|
||||
// parents: Vec<parent::Model>,
|
||||
) -> Result<ApplicationDetails, ServiceError> {
|
||||
let candidate = Query::find_candidate_by_id(db, application_id).await?
|
||||
.ok_or(ServiceError::CandidateNotFound)?;
|
||||
let parent = Query::find_candidate_parents(db, candidate.clone()).await?; // TODO
|
||||
let enc_details = EncryptedApplicationDetails::try_from((candidate, parent[0].clone()))?;
|
||||
let parents = Query::find_candidate_parents(db, candidate.clone()).await?;
|
||||
let enc_details = EncryptedApplicationDetails::try_from((candidate, parents))?;
|
||||
|
||||
enc_details.decrypt(private_key).await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use entity::candidate;
|
|||
use sea_orm::{prelude::Uuid, DbConn};
|
||||
|
||||
use crate::{
|
||||
models::candidate_details::{EncryptedApplicationDetails, EncryptedString, EncryptedCandidateDetails},
|
||||
models::{candidate_details::{EncryptedApplicationDetails, EncryptedString, EncryptedCandidateDetails}, candidate::CandidateDetails},
|
||||
crypto::{self, hash_password},
|
||||
error::ServiceError,
|
||||
Mutation, Query, models::candidate::{BaseCandidateResponse, CreateCandidateResponse}, utils::db::get_recipients,
|
||||
|
|
@ -102,7 +102,7 @@ impl CandidateService {
|
|||
) -> Result<CreateCandidateResponse, ServiceError> {
|
||||
let candidate = Query::find_candidate_by_id(db, id).await?
|
||||
.ok_or(ServiceError::CandidateNotFound)?;
|
||||
let parent = Query::find_candidate_parents(db, candidate.clone()).await?; // TODO
|
||||
let parents = Query::find_candidate_parents(db, candidate.clone()).await?; // TODO
|
||||
|
||||
|
||||
let new_password_plain = crypto::random_8_char_string();
|
||||
|
|
@ -124,12 +124,12 @@ impl CandidateService {
|
|||
.await?;
|
||||
|
||||
let enc_details_opt = EncryptedApplicationDetails::try_from(
|
||||
(candidate, parent[0].clone())
|
||||
(candidate.clone(), parents)
|
||||
);
|
||||
|
||||
if let Ok(enc_details) = enc_details_opt {
|
||||
let application_details = enc_details.decrypt(admin_private_key).await?;
|
||||
ApplicationService::add_all_details(db, id, &application_details).await?;
|
||||
ApplicationService::add_all_details(db, candidate, &application_details).await?;
|
||||
}
|
||||
|
||||
Ok(
|
||||
|
|
@ -149,8 +149,10 @@ impl CandidateService {
|
|||
pub(in crate::services) async fn add_candidate_details(
|
||||
db: &DbConn,
|
||||
candidate: candidate::Model,
|
||||
enc_details: EncryptedCandidateDetails,
|
||||
details: &CandidateDetails,
|
||||
) -> Result<entity::candidate::Model, ServiceError> {
|
||||
let recipients = get_recipients(db, &candidate.public_key).await?;
|
||||
let enc_details = EncryptedCandidateDetails::new(&details, recipients).await?;
|
||||
let model = Mutation::add_candidate_details(db, candidate, enc_details).await?;
|
||||
Ok(model)
|
||||
}
|
||||
|
|
@ -372,8 +374,8 @@ pub mod tests {
|
|||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub async fn put_user_data(db: &DbConn) -> (candidate::Model, parent::Model) {
|
||||
use crate::models::candidate_details::tests::APPLICATION_DETAILS;
|
||||
pub async fn put_user_data(db: &DbConn) -> (candidate::Model, Vec<parent::Model>) {
|
||||
use crate::{models::candidate_details::tests::APPLICATION_DETAILS, Query};
|
||||
|
||||
let plain_text_password = "test".to_string();
|
||||
let (candidate, _parent) = ApplicationService::create_candidate_with_parent(
|
||||
|
|
@ -382,23 +384,28 @@ pub mod tests {
|
|||
&plain_text_password,
|
||||
"".to_string(),
|
||||
)
|
||||
.await
|
||||
.ok()
|
||||
.unwrap();
|
||||
.await
|
||||
.ok()
|
||||
.unwrap();
|
||||
|
||||
let form = APPLICATION_DETAILS.lock().unwrap().clone();
|
||||
|
||||
ApplicationService::add_all_details(&db, candidate.application, &form)
|
||||
let (candidate, parents) = ApplicationService::add_all_details(&db, candidate.clone(), &form)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
(
|
||||
candidate,
|
||||
parents,
|
||||
)
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_put_user_data() {
|
||||
let db = get_memory_sqlite_connection().await;
|
||||
let (candidate, parent) = put_user_data(&db).await;
|
||||
let (candidate, parents) = put_user_data(&db).await;
|
||||
assert!(candidate.name.is_some());
|
||||
assert!(parent.name.is_some());
|
||||
assert!(parents[0].name.is_some());
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use entity::{parent};
|
||||
use entity::{parent, candidate};
|
||||
use sea_orm::DbConn;
|
||||
|
||||
use crate::{error::ServiceError, Mutation, models::candidate_details::{EncryptedParentDetails}};
|
||||
use crate::{error::ServiceError, Mutation, models::{candidate_details::{EncryptedParentDetails}, candidate::ParentDetails}, Query, utils::db::get_recipients};
|
||||
|
||||
pub struct ParentService;
|
||||
|
||||
|
|
@ -16,7 +16,7 @@ impl ParentService {
|
|||
Ok(parent)
|
||||
}
|
||||
|
||||
pub async fn add_parent_details(
|
||||
/* pub async fn add_parent_details(
|
||||
db: &DbConn,
|
||||
parent: parent::Model,
|
||||
enc_details: EncryptedParentDetails,
|
||||
|
|
@ -25,5 +25,42 @@ impl ParentService {
|
|||
.await?;
|
||||
|
||||
Ok(parent)
|
||||
} */
|
||||
pub async fn add_parents_details(
|
||||
db: &DbConn,
|
||||
ref_candidate: candidate::Model,
|
||||
parents_details: &Vec<ParentDetails>,
|
||||
) -> Result<Vec<parent::Model>, ServiceError> {
|
||||
let found_parents = Query::find_candidate_parents(db, ref_candidate.clone()).await?;
|
||||
if found_parents.len() > 2 {
|
||||
return Err(ServiceError::ParentOverflow);
|
||||
}
|
||||
|
||||
let mut result = vec![];
|
||||
for i in 0..parents_details.len() {
|
||||
let found_parent = match found_parents.get(i) {
|
||||
Some(parent) => parent.clone(),
|
||||
None => ParentService::create(db, ref_candidate.application).await?,
|
||||
};
|
||||
let recipients = get_recipients(db, &ref_candidate.public_key).await?;
|
||||
let enc_details = EncryptedParentDetails::new(&parents_details[i], recipients).await?;
|
||||
let parent = Mutation::add_parent_details(db, found_parent.clone(), enc_details.clone()).await?;
|
||||
result.push(parent);
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::{utils::db::get_memory_sqlite_connection, models::{candidate::{ParentDetails, ApplicationDetails}, candidate_details::{tests::APPLICATION_DETAILS, EncryptedParentDetails, EncryptedApplicationDetails}}, services::{candidate_service::CandidateService, application_service::ApplicationService}};
|
||||
|
||||
#[tokio::test]
|
||||
async fn create_parent_test() {
|
||||
let db = get_memory_sqlite_connection().await;
|
||||
CandidateService::create(&db, 103100, &"test".to_string(), "".to_string()).await.unwrap();
|
||||
super::ParentService::create(&db, 103100).await.unwrap();
|
||||
super::ParentService::create(&db, 103100).await.unwrap();
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,8 @@ use sea_orm::entity::prelude::*;
|
|||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel)]
|
||||
#[sea_orm(table_name = "parent")]
|
||||
pub struct Model {
|
||||
#[sea_orm(primary_key, auto_increment = false)]
|
||||
#[sea_orm(primary_key)]
|
||||
pub id: i32,
|
||||
pub application: i32,
|
||||
pub name: Option<String>,
|
||||
pub surname: Option<String>,
|
||||
|
|
|
|||
Loading…
Reference in a new issue